Merge pull request #1793 from coralproject/auth-views

[next] Auth views
This commit is contained in:
Kim Gardner
2018-08-28 13:50:59 -04:00
committed by GitHub
154 changed files with 7550 additions and 1847 deletions
+3
View File
@@ -19,6 +19,9 @@ const config: Config = {
"core/client/stream/**/*.ts",
"core/client/stream/**/*.tsx",
"core/client/stream/**/*.graphql",
"core/client/auth/**/*.ts",
"core/client/auth/**/*.tsx",
"core/client/auth/**/*.graphql",
"core/server/**/*.graphql",
],
ignore: [
+86 -73
View File
@@ -5681,6 +5681,12 @@
"ylru": "^1.2.0"
}
},
"cached-iterable": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/cached-iterable/-/cached-iterable-0.2.1.tgz",
"integrity": "sha512-8zAVjMjdn/S/QXJaOnqsko0+ZJzXT2Dum2u9TMGg5YR9fxONPrUjuO9VYqnb1AoldXeYVAcNJLgT5Q8WaIJSgA==",
"dev": true
},
"call-me-maybe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
@@ -9943,9 +9949,9 @@
"integrity": "sha1-A5/fI/iCPkQMOPMnfm/vEXQhWzA="
},
"fluent": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/fluent/-/fluent-0.6.4.tgz",
"integrity": "sha512-EXfMJmnGbUgaIC1myIzDk5akAF6+1JrI7KVnNCba2ou7WCKc/2CWa8QshfhImVtettOvEs0z0UVdMrS6zX7pxA==",
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/fluent/-/fluent-0.8.0.tgz",
"integrity": "sha512-bZfthhubEH1lKgGIi0fIDeNkZrfEOu3MrLbi284LdxNG+9Q5gq2KsuoocumqNPStVtWo3S3/1p8RIqd34u3Mzw==",
"dev": true
},
"fluent-intl-polyfill": {
@@ -9964,15 +9970,22 @@
"dev": true
},
"fluent-react": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/fluent-react/-/fluent-react-0.7.0.tgz",
"integrity": "sha512-XgAG06hcVW6oQu3NqLB4KACFBDC9broXG4XDP2xqmj+/DPmZlhHMMD73tFz1mBxCs1pLeojmsYdgyl8l6fF4SA==",
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/fluent-react/-/fluent-react-0.8.0.tgz",
"integrity": "sha512-mO8iPb+muWopEDcbNgb66kr6Tl9tPjRpguDaez9W0xU1hiJ9fQ6EfdCxCUQOYsnVnbruLJqxaRsExSRrCamLCg==",
"dev": true,
"requires": {
"fluent": "^0.4.0 || ^0.6.0",
"cached-iterable": "^0.2.1",
"fluent-sequence": "^0.2.0",
"prop-types": "^15.6.0"
}
},
"fluent-sequence": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/fluent-sequence/-/fluent-sequence-0.2.0.tgz",
"integrity": "sha512-t8fc4rHvzO9Yk8otP8LkTqWo6mzjdemooQcnHlDrNzrYAnwsSbVFIlthnkzK2pLsRnO9Ybmw4lXOYUx+fAIyJw==",
"dev": true
},
"flush-write-stream": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
@@ -10167,24 +10180,24 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
"version": "1.2.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"optional": true,
"requires": {
@@ -10194,12 +10207,12 @@
},
"balanced-match": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": false,
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
@@ -10208,34 +10221,34 @@
},
"chownr": {
"version": "1.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"concat-map": {
"version": "0.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-util-is": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"optional": true
},
"debug": {
"version": "2.6.9",
"resolved": false,
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"optional": true,
"requires": {
@@ -10244,25 +10257,25 @@
},
"deep-extend": {
"version": "0.5.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"resolved": false,
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"resolved": false,
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"optional": true,
"requires": {
@@ -10271,13 +10284,13 @@
},
"fs.realpath": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"optional": true
},
"gauge": {
"version": "2.7.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true,
"requires": {
@@ -10293,7 +10306,7 @@
},
"glob": {
"version": "7.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"optional": true,
"requires": {
@@ -10307,13 +10320,13 @@
},
"has-unicode": {
"version": "2.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"resolved": false,
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz",
"integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
"optional": true,
"requires": {
@@ -10322,7 +10335,7 @@
},
"ignore-walk": {
"version": "3.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"optional": true,
"requires": {
@@ -10331,7 +10344,7 @@
},
"inflight": {
"version": "1.0.6",
"resolved": false,
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"optional": true,
"requires": {
@@ -10341,18 +10354,18 @@
},
"inherits": {
"version": "2.0.3",
"resolved": false,
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.5",
"resolved": false,
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
@@ -10360,13 +10373,13 @@
},
"isarray": {
"version": "1.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
},
"minimatch": {
"version": "3.0.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
@@ -10374,12 +10387,12 @@
},
"minimist": {
"version": "0.0.8",
"resolved": false,
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"minipass": {
"version": "2.2.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
"requires": {
"safe-buffer": "^5.1.1",
@@ -10388,7 +10401,7 @@
},
"minizlib": {
"version": "1.1.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz",
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
"optional": true,
"requires": {
@@ -10397,7 +10410,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
@@ -10405,13 +10418,13 @@
},
"ms": {
"version": "2.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"optional": true
},
"needle": {
"version": "2.2.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz",
"integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
"optional": true,
"requires": {
@@ -10422,7 +10435,7 @@
},
"node-pre-gyp": {
"version": "0.10.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz",
"integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
"optional": true,
"requires": {
@@ -10440,7 +10453,7 @@
},
"nopt": {
"version": "4.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"optional": true,
"requires": {
@@ -10450,13 +10463,13 @@
},
"npm-bundled": {
"version": "1.0.3",
"resolved": false,
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz",
"integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"resolved": false,
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz",
"integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
"optional": true,
"requires": {
@@ -10466,7 +10479,7 @@
},
"npmlog": {
"version": "4.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true,
"requires": {
@@ -10478,18 +10491,18 @@
},
"number-is-nan": {
"version": "1.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"optional": true
},
"once": {
"version": "1.4.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
@@ -10497,19 +10510,19 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"optional": true
},
"osenv": {
"version": "0.1.5",
"resolved": false,
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"optional": true,
"requires": {
@@ -10519,19 +10532,19 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"optional": true
},
"rc": {
"version": "1.2.7",
"resolved": false,
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz",
"integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
"optional": true,
"requires": {
@@ -10543,7 +10556,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"optional": true
}
@@ -10551,7 +10564,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": false,
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"optional": true,
"requires": {
@@ -10566,7 +10579,7 @@
},
"rimraf": {
"version": "2.6.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"optional": true,
"requires": {
@@ -10575,42 +10588,42 @@
},
"safe-buffer": {
"version": "5.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"optional": true
},
"sax": {
"version": "1.2.4",
"resolved": false,
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true
},
"semver": {
"version": "5.5.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": false,
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"optional": true
},
"string-width": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
@@ -10620,7 +10633,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"optional": true,
"requires": {
@@ -10629,7 +10642,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@@ -10637,13 +10650,13 @@
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"optional": true
},
"tar": {
"version": "4.4.1",
"resolved": false,
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz",
"integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
"optional": true,
"requires": {
@@ -10658,13 +10671,13 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true
},
"wide-align": {
"version": "1.1.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
"optional": true,
"requires": {
@@ -10673,12 +10686,12 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "3.0.2",
"resolved": false,
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
}
}
+2 -2
View File
@@ -151,10 +151,10 @@
"eventemitter2": "^5.0.1",
"final-form": "^4.8.1",
"flat": "^4.1.0",
"fluent": "^0.6.4",
"fluent": "^0.8.0",
"fluent-intl-polyfill": "^0.1.0",
"fluent-langneg": "^0.1.0",
"fluent-react": "^0.7.0",
"fluent-react": "^0.8.0",
"graphql-playground-middleware-express": "^1.7.2",
"graphql-schema-typescript": "^1.2.1",
"gulp": "^4.0.0",
+3
View File
@@ -20,12 +20,15 @@ export default {
appLocales: resolveSrc("locales"),
appThemeVariables: resolveSrc("core/client/ui/theme/variables.ts"),
appThemeVariablesCSS: resolveSrc("core/client/ui/theme/variables.css"),
appStreamHTML: resolveSrc("core/client/stream/index.html"),
appStreamLocalesTemplate: resolveSrc("core/client/stream/locales.ts"),
appStreamIndex: resolveSrc("core/client/stream/index.tsx"),
appAuthHTML: resolveSrc("core/client/auth/index.html"),
appAuthLocalesTemplate: resolveSrc("core/client/auth/locales.ts"),
appAuthIndex: resolveSrc("core/client/auth/index.tsx"),
appEmbedIndex: resolveSrc("core/client/embed/index.ts"),
appEmbedHTML: resolveSrc("core/client/embed/index.html"),
+4 -1
View File
@@ -1,7 +1,7 @@
/* Here we add global stylings for body and document */
:global {
body {
margin: "0";
margin: 0;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
@@ -12,4 +12,7 @@
.root {
width: 100%;
padding: var(--spacing-unit) var(--spacing-unit) calc(2 * var(--spacing-unit))
var(--spacing-unit);
box-sizing: border-box;
}
@@ -0,0 +1,14 @@
import { shallow } from "enzyme";
import React from "react";
import { PropTypesOf } from "talk-framework/types";
import App from "./App";
it("renders sign in", () => {
const props: PropTypesOf<typeof App> = {
view: "SIGN_IN",
};
const wrapper = shallow(<App {...props} />);
expect(wrapper).toMatchSnapshot();
});
+33 -14
View File
@@ -1,22 +1,41 @@
import * as React from "react";
import { StatelessComponent } from "react";
import { Flex } from "talk-ui/components";
import React, { StatelessComponent } from "react";
import * as styles from "./App.css";
import ForgotPasswordContainer from "../containers/ForgotPasswordContainer";
import ResetPasswordContainer from "../containers/ResetPasswordContainer";
import SignInContainer from "../containers/SignInContainer";
import SignUpContainer from "../containers/SignUpContainer";
// TODO: (cvle) Remove %future added value when we have Relay 1.6
// https://github.com/facebook/relay/commit/1e87e43add7667a494f7ff4cfa7f03f1ab8d81a2
export type View =
| "SIGN_UP"
| "SIGN_IN"
| "FORGOT_PASSWORD"
| "RESET_PASSWORD"
| "%future added value";
export interface AppProps {
// TODO: (cvle) Remove %future added value when we have Relay 1.6
// https://github.com/facebook/relay/commit/1e87e43add7667a494f7ff4cfa7f03f1ab8d81a2
view: "SIGN_UP" | "SIGN_IN" | "FORGOT_PASSWORD" | "%future added value";
view: View;
}
const App: StatelessComponent<AppProps> = props => {
return (
<Flex justifyContent="center" className={styles.root}>
Current View: {props.view}
</Flex>
);
const renderView = (view: View) => {
switch (view) {
case "SIGN_UP":
return <SignUpContainer />;
case "SIGN_IN":
return <SignInContainer />;
case "FORGOT_PASSWORD":
return <ForgotPasswordContainer />;
case "RESET_PASSWORD":
return <ResetPasswordContainer />;
default:
throw new Error(`Unknown view ${view}`);
}
};
const App: StatelessComponent<AppProps> = ({ view }) => (
<div className={styles.root}>{renderView(view)}</div>
);
export default App;
@@ -0,0 +1,110 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { Field, Form } from "react-final-form";
import { OnSubmit } from "talk-framework/lib/form";
import {
composeValidators,
required,
validateEmail,
} from "talk-framework/lib/validation";
import {
Button,
CallOut,
FormField,
HorizontalGutter,
InputLabel,
TextField,
Typography,
ValidationMessage,
} from "talk-ui/components";
import AutoHeightContainer from "../containers/AutoHeightContainer";
interface FormProps {
email: string;
}
export interface ForgotPasswordForm {
onSubmit: OnSubmit<FormProps>;
}
const ForgotPassword: StatelessComponent<ForgotPasswordForm> = props => {
return (
<Form onSubmit={props.onSubmit}>
{({ handleSubmit, submitting, submitError }) => (
<form autoComplete="off" onSubmit={handleSubmit}>
<AutoHeightContainer />
<HorizontalGutter size="double">
<Localized id="forgotPassword-forgotPasswordHeader">
<Typography variant="heading1" align="center">
Forgot Password
</Typography>
</Localized>
<Localized id="forgotPassword-enterEmailAndGetALink">
<Typography variant="bodyCopy">
Enter your email address below and we will send you a link to
reset your password.
</Typography>
</Localized>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Field
name="email"
validate={composeValidators(required, validateEmail)}
>
{({ input, meta }) => (
<FormField>
<Localized id="forgotPassword-emailAddressLabel">
<InputLabel>Email Address</InputLabel>
</Localized>
<Localized
id="forgotPassword-emailAddressTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Email Address"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
fullWidth
disabled={submitting}
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Localized id="forgotPassword-sendEmailButton">
<Button
variant="filled"
color="primary"
size="large"
fullWidth
disabled={submitting}
>
Send Email
</Button>
</Localized>
</HorizontalGutter>
</form>
)}
</Form>
);
};
export default ForgotPassword;
@@ -0,0 +1,154 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { Field, Form } from "react-final-form";
import { OnSubmit } from "talk-framework/lib/form";
import {
composeValidators,
required,
validateEqualPasswords,
validatePassword,
} from "talk-framework/lib/validation";
import {
Button,
CallOut,
FormField,
HorizontalGutter,
InputDescription,
InputLabel,
TextField,
Typography,
ValidationMessage,
} from "talk-ui/components";
import AutoHeightContainer from "../containers/AutoHeightContainer";
interface FormProps {
password: string;
confirmPassword: string;
}
export interface ResetPasswordForm {
onSubmit: OnSubmit<FormProps>;
}
const ResetPassword: StatelessComponent<ResetPasswordForm> = props => {
return (
<Form onSubmit={props.onSubmit}>
{({ handleSubmit, submitting, submitError }) => (
<form autoComplete="off" onSubmit={handleSubmit}>
<AutoHeightContainer />
<HorizontalGutter size="double">
<Localized id="resetPassword-resetPasswordHeader">
<Typography variant="heading1" align="center">
Reset Password
</Typography>
</Localized>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Field
name="password"
validate={composeValidators(required, validatePassword)}
>
{({ input, meta }) => (
<FormField>
<Localized id="resetPassword-passwordLabel">
<InputLabel>Password</InputLabel>
</Localized>
<Localized
id="resetPassword-passwordDescription"
$minLength={8}
>
<InputDescription>
{"Must be at least {$minLength} characters"}
</InputDescription>
</Localized>
<Localized
id="resetPassword-passwordTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Password"
type="password"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Field
name="confirmPassword"
validate={composeValidators(required, validateEqualPasswords)}
>
{({ input, meta }) => (
<FormField>
<Localized id="resetPassword-confirmPasswordLabel">
<InputLabel>Confirm Password</InputLabel>
</Localized>
<Localized
id="resetPassword-confirmPasswordTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Confirm Password"
type="password"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Localized id="resetPassword-resetPasswordButton">
<Button
variant="filled"
color="primary"
size="large"
fullWidth
disabled={submitting}
>
Reset Password
</Button>
</Localized>
</HorizontalGutter>
</form>
)}
</Form>
);
};
export default ResetPassword;
+179
View File
@@ -0,0 +1,179 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { Field, Form } from "react-final-form";
import { OnSubmit } from "talk-framework/lib/form";
import {
composeValidators,
required,
validateEmail,
} from "talk-framework/lib/validation";
import {
Button,
CallOut,
Flex,
FormField,
HorizontalGutter,
InputLabel,
TextField,
Typography,
ValidationMessage,
} from "talk-ui/components";
import AutoHeightContainer from "../containers/AutoHeightContainer";
interface FormProps {
email: string;
password: string;
}
export interface SignInForm {
onSubmit: OnSubmit<FormProps>;
onGotoSignUp: () => void;
onGotoForgotPassword: () => void;
}
const SignIn: StatelessComponent<SignInForm> = props => {
return (
<Form onSubmit={props.onSubmit}>
{({ handleSubmit, submitting, submitError }) => (
<form autoComplete="off" onSubmit={handleSubmit}>
<AutoHeightContainer />
<HorizontalGutter size="double">
<Localized id="signIn-signInToJoinHeader">
<Typography variant="heading1" align="center">
Sign in to join the conversation
</Typography>
</Localized>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Field
name="email"
validate={composeValidators(required, validateEmail)}
>
{({ input, meta }) => (
<FormField>
<Localized id="signIn-emailAddressLabel">
<InputLabel>Email Address</InputLabel>
</Localized>
<Localized
id="signIn-emailAddressTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Email Address"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Field name="password" validate={composeValidators(required)}>
{({ input, meta }) => (
<FormField>
<Localized id="signIn-passwordLabel">
<InputLabel>Password</InputLabel>
</Localized>
<Localized
id="signIn-passwordTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Password"
type="password"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
<Flex justifyContent="flex-end">
<Localized id="signIn-forgotYourPassword">
<Button
id="signIn-gotoForgotPasswordButton"
variant="underlined"
color="primary"
size="small"
disabled={submitting}
onClick={props.onGotoForgotPassword}
>
Forgot your password?
</Button>
</Localized>
</Flex>
</FormField>
)}
</Field>
<Localized id="signIn-signInAndJoinButton">
<Button
variant="filled"
color="primary"
size="large"
type="submit"
disabled={submitting}
fullWidth
>
Sign in and join the conversation
</Button>
</Localized>
<Flex justifyContent="center">
<Localized
id="signIn-noAccountSignUp"
button={
<Button
id="signIn-gotoSignUpButton"
variant="underlined"
size="small"
color="primary"
disabled={submitting}
onClick={props.onGotoSignUp}
/>
}
>
<Typography variant="bodyCopy" container={Flex}>
{"Don't have an account? <button>Sign Up</button>"}
</Typography>
</Localized>
</Flex>
</HorizontalGutter>
</form>
)}
</Form>
);
};
export default SignIn;
+253
View File
@@ -0,0 +1,253 @@
import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import { Field, Form } from "react-final-form";
import { OnSubmit } from "talk-framework/lib/form";
import {
composeValidators,
required,
validateEmail,
validateEqualPasswords,
validatePassword,
validateUsername,
} from "talk-framework/lib/validation";
import {
Button,
CallOut,
Flex,
FormField,
HorizontalGutter,
InputDescription,
InputLabel,
TextField,
Typography,
ValidationMessage,
} from "talk-ui/components";
import AutoHeightContainer from "../containers/AutoHeightContainer";
interface FormProps {
email: string;
username: string;
password: string;
confirmPassword: string;
}
export interface SignUpForm {
onSubmit: OnSubmit<FormProps>;
onGotoSignIn: () => void;
}
const SignUp: StatelessComponent<SignUpForm> = props => {
return (
<Form onSubmit={props.onSubmit}>
{({ handleSubmit, submitting, submitError }) => (
<form autoComplete="off" onSubmit={handleSubmit}>
<AutoHeightContainer />
<HorizontalGutter size="double">
<Localized id="signUp-signUpToJoinHeader">
<Typography variant="heading1" align="center">
Sign up to join the conversation
</Typography>
</Localized>
{submitError && (
<CallOut color="error" fullWidth>
{submitError}
</CallOut>
)}
<Field
name="email"
validate={composeValidators(required, validateEmail)}
>
{({ input, meta }) => (
<FormField>
<Localized id="signUp-emailAddressLabel">
<InputLabel>Email Address</InputLabel>
</Localized>
<Localized
id="signUp-emailAddressTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Email Address"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Field
name="username"
validate={composeValidators(required, validateUsername)}
>
{({ input, meta }) => (
<FormField>
<Localized id="signUp-usernameLabel">
<InputLabel>Username</InputLabel>
</Localized>
<Localized id="signUp-usernameDescription">
<InputDescription>
A unique identifier displayed on your comments. You may
use _ and .
</InputDescription>
</Localized>
<Localized
id="signUp-usernameTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Username"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Field
name="password"
validate={composeValidators(required, validatePassword)}
>
{({ input, meta }) => (
<FormField>
<Localized id="signUp-passwordLabel">
<InputLabel>Password</InputLabel>
</Localized>
<Localized id="signUp-passwordDescription" $minLength={8}>
<InputDescription>
{"Must be at least {$minLength} characters"}
</InputDescription>
</Localized>
<Localized
id="signUp-passwordTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Password"
type="password"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Field
name="confirmPassword"
validate={composeValidators(required, validateEqualPasswords)}
>
{({ input, meta }) => (
<FormField>
<Localized id="signUp-confirmPasswordLabel">
<InputLabel>Confirm Password</InputLabel>
</Localized>
<Localized
id="signUp-confirmPasswordTextField"
attrs={{ placeholder: true }}
>
<TextField
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Confirm Password"
type="password"
color={
meta.touched && (meta.error || meta.submitError)
? "error"
: "regular"
}
disabled={submitting}
fullWidth
/>
</Localized>
{meta.touched &&
(meta.error || meta.submitError) && (
<ValidationMessage fullWidth>
{meta.error || meta.submitError}
</ValidationMessage>
)}
</FormField>
)}
</Field>
<Localized id="signUp-signUpAndJoinButton">
<Button
variant="filled"
color="primary"
size="large"
type="submit"
disabled={submitting}
fullWidth
>
Sign up and join the conversation
</Button>
</Localized>
<Flex justifyContent="center">
<Localized
id="signUp-accountAvailableSignIn"
button={
<Button
id="signUp-gotoSignInButton"
variant="underlined"
size="small"
color="primary"
onClick={props.onGotoSignIn}
disabled={submitting}
/>
}
>
<Typography variant="bodyCopy" container={Flex}>
{"Already have an account? <button>Sign In </button>"}
</Typography>
</Localized>
</Flex>
</HorizontalGutter>
</form>
)}
</Form>
);
};
export default SignUp;
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders sign in 1`] = `
<div
className="App-root"
>
<withContext(createMutationContainer(withContext(createMutationContainer(SignInContainer)))) />
</div>
`;
@@ -0,0 +1,25 @@
import { Component } from "react";
import resizePopup from "../dom/resizePopup";
/**
* A container that adapts the window height to the current body size
* when this is mounted or updated.
*/
export default class AutoHeightContainer extends Component {
private updateWindowSize() {
window.requestAnimationFrame(() => setTimeout(resizePopup, 0));
}
public componentDidMount() {
this.updateWindowSize();
}
public componentDidUpdate() {
this.updateWindowSize();
}
public render() {
return null;
}
}
@@ -0,0 +1,11 @@
import React, { Component } from "react";
import ForgotPassword from "../components/ForgotPassword";
class ForgotPasswordContainer extends Component {
public render() {
// tslint:disable-next-line:no-empty
return <ForgotPassword onSubmit={() => {}} />;
}
}
export default ForgotPasswordContainer;
@@ -0,0 +1,11 @@
import React, { Component } from "react";
import ResetPassword from "../components/ResetPassword";
class ResetPasswordContainer extends Component {
public render() {
// tslint:disable-next-line:no-empty
return <ResetPassword onSubmit={() => {}} />;
}
}
export default ResetPasswordContainer;
@@ -0,0 +1,40 @@
import { FORM_ERROR } from "final-form";
import React, { Component } from "react";
import SignIn, { SignInForm } from "../components/SignIn";
import {
SetViewMutation,
SignInMutation,
withSetViewMutation,
withSignInMutation,
} from "../mutations";
interface SignInContainerProps {
signIn: SignInMutation;
setView: SetViewMutation;
}
class SignInContainer extends Component<SignInContainerProps> {
private onSubmit: SignInForm["onSubmit"] = async (input, form) => {
try {
await this.props.signIn(input);
return form.reset();
} catch (error) {
return { [FORM_ERROR]: error.message };
}
};
private goToForgotPassword = () =>
this.props.setView({ view: "FORGOT_PASSWORD" });
private goToSignUp = () => this.props.setView({ view: "SIGN_UP" });
public render() {
return (
<SignIn
onSubmit={this.onSubmit}
onGotoForgotPassword={this.goToForgotPassword}
onGotoSignUp={this.goToSignUp}
/>
);
}
}
const enhanced = withSetViewMutation(withSignInMutation(SignInContainer));
export default enhanced;
@@ -0,0 +1,33 @@
import { FORM_ERROR } from "final-form";
import React, { Component } from "react";
import SignUp, { SignUpForm } from "../components/SignUp";
import {
SetViewMutation,
SignUpMutation,
withSetViewMutation,
withSignUpMutation,
} from "../mutations";
interface SignUpContainerProps {
signUp: SignUpMutation;
setView: SetViewMutation;
}
class SignUpContainer extends Component<SignUpContainerProps> {
private onSubmit: SignUpForm["onSubmit"] = async (input, form) => {
try {
return await this.props.signUp(input);
form.reset();
} catch (error) {
return { [FORM_ERROR]: error.message };
}
};
private goToSignIn = () => this.props.setView({ view: "SIGN_IN" });
public render() {
return <SignUp onSubmit={this.onSubmit} onGotoSignIn={this.goToSignIn} />;
}
}
const enhanced = withSetViewMutation(withSignUpMutation(SignUpContainer));
export default enhanced;
+17
View File
@@ -0,0 +1,17 @@
function resizePopup() {
const innerHeight = window.document.body.offsetHeight;
window.resizeTo(
window.outerWidth,
innerHeight + window.outerHeight - window.innerHeight
);
}
let resizedAlready = false;
export default function resizeOncePerFrame() {
if (resizedAlready) {
return;
}
resizedAlready = true;
requestAnimationFrame(() => setTimeout(() => (resizedAlready = false), 0));
resizePopup();
}
+21
View File
@@ -9,9 +9,29 @@ import {
} from "talk-framework/lib/bootstrap";
import AppContainer from "./containers/AppContainer";
import resizePopup from "./dom/resizePopup";
import { initLocalState } from "./local";
import localesData from "./locales";
/**
* Adapt popup height to current content every 100ms.
*
* The goal is to smooth out height inconsistensies e.g. when fonts
* are switched out or other resources being loaded that React has no influence
* over.
*
* This works in addition to <AutoHeightContainer /> which
* adapt popup height when React does an update.
*/
function pollPopupHeight(interval: number = 100) {
setTimeout(() => {
window.requestAnimationFrame(() => {
resizePopup();
pollPopupHeight(interval);
});
}, interval);
}
// This is called when the context is first initialized.
async function init({ relayEnvironment }: TalkContext) {
await initLocalState(relayEnvironment);
@@ -32,6 +52,7 @@ async function main() {
);
ReactDOM.render(<Index />, document.getElementById("app"));
pollPopupHeight();
}
main();
+1
View File
@@ -2,6 +2,7 @@ enum View {
SIGN_UP
SIGN_IN
FORGOT_PASSWORD
RESET_PASSWORD
}
type Local {
@@ -6,7 +6,7 @@ import { LOCAL_ID } from "talk-framework/lib/relay/withLocalStateContainer";
export interface SetViewInput {
// TODO: replace with generated typescript types.
view: "SIGN_IN" | "SIGN_UP" | "FORGOT_PASSWORD";
view: "SIGN_IN" | "SIGN_UP" | "FORGOT_PASSWORD" | "RESET_PASSWORD";
}
export type SetViewMutation = (input: SetViewInput) => Promise<void>;
@@ -17,6 +17,7 @@ export async function commit(
window.close();
} catch (err) {
postMessage.send("authError", err.toString(), window.opener);
throw err;
}
}
@@ -1,3 +1,4 @@
import { pick } from "lodash";
import { Environment } from "relay-runtime";
import { TalkContext } from "talk-framework/lib/bootstrap";
@@ -12,11 +13,15 @@ export async function commit(
{ rest, postMessage }: TalkContext
) {
try {
const result = await signUp(rest, input);
const result = await signUp(
rest,
pick(input, "email", "password", "username")
);
postMessage.send("setAuthToken", result.token, window.opener);
window.close();
} catch (err) {
postMessage.send("authError", err.toString(), window.opener);
throw err;
}
}
@@ -0,0 +1,971 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`accepts correct password 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value="hans@test.com"
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value="testtest"
/>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`accepts valid email 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value="hans@test.com"
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorError TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="ValidationMessage-root ValidationMessage-colorError ValidationMessage-fullWidth"
>
<span
className="Icon-root ValidationMessage-icon Icon-sm"
>
warning
</span>
<span>
This field is required.
</span>
</div>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`checks for invalid email 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorError TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value="invalid-email"
/>
<div
className="ValidationMessage-root ValidationMessage-colorError ValidationMessage-fullWidth"
>
<span
className="Icon-root ValidationMessage-icon Icon-sm"
>
warning
</span>
<span>
Please enter a valid email address.
</span>
</div>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorError TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="ValidationMessage-root ValidationMessage-colorError ValidationMessage-fullWidth"
>
<span
className="Icon-root ValidationMessage-icon Icon-sm"
>
warning
</span>
<span>
This field is required.
</span>
</div>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`renders sign in form 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value=""
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`shows error when submitting empty form 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorError TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value=""
/>
<div
className="ValidationMessage-root ValidationMessage-colorError ValidationMessage-fullWidth"
>
<span
className="Icon-root ValidationMessage-icon Icon-sm"
>
warning
</span>
<span>
This field is required.
</span>
</div>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorError TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="ValidationMessage-root ValidationMessage-colorError ValidationMessage-fullWidth"
>
<span
className="Icon-root ValidationMessage-icon Icon-sm"
>
warning
</span>
<span>
This field is required.
</span>
</div>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`shows server error 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={true}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value="hans@test.com"
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={true}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value="testtest"
/>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined Button-disabled"
disabled={true}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`shows server error 2`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
Server Error
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value="hans@test.com"
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value="testtest"
/>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`submits form successfully 1`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="CallOut-root CallOut-colorError CallOut-fullWidth"
>
Server Error
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={true}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value="hans@test.com"
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={true}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value="testtest"
/>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined Button-disabled"
disabled={true}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
exports[`submits form successfully 2`] = `
<div
className="App-root"
>
<form
autoComplete="off"
onSubmit={[Function]}
>
<div
className="HorizontalGutter-root HorizontalGutter-double"
>
<h1
className="Typography-root Typography-heading1 Typography-colorTextPrimary Typography-alignCenter"
>
Sign in to join the conversation
</h1>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Email Address
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="email"
onChange={[Function]}
placeholder="Email Address"
type="text"
value=""
/>
</div>
<div
className="HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Password
</label>
<input
className="TextField-root TextField-colorRegular TextField-fullWidth"
disabled={false}
name="password"
onChange={[Function]}
placeholder="Password"
type="password"
value=""
/>
<div
className="Flex-root Flex-flex Flex-justifyFlexEnd"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
disabled={false}
id="signIn-gotoForgotPasswordButton"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Forgot your password?
</button>
</div>
</div>
<button
className="BaseButton-root Button-root Button-sizeLarge Button-colorPrimary Button-variantFilled Button-fullWidth"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
<div
className="Flex-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-root Typography-root Typography-bodyCopy Typography-colorTextPrimary Flex-flex"
>
<span>
Don't have an account? &lt;button&gt;Sign Up&lt;/button&gt;
</span>
</div>
</div>
</div>
</form>
</div>
`;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,28 @@
import { IResolvers } from "graphql-tools";
import { Environment, RecordProxy, RecordSourceProxy } from "relay-runtime";
import { createRelayEnvironment } from "talk-framework/testHelpers";
interface CreateEnvironmentParams {
logNetwork?: boolean;
resolvers?: IResolvers<any, any>;
initLocalState?: (
local: RecordProxy,
source: RecordSourceProxy,
environment: Environment
) => void;
}
export default function createEnvironment(params: CreateEnvironmentParams) {
return createRelayEnvironment({
network: {
logNetwork: params.logNetwork,
resolvers: params.resolvers || {},
projectName: "tenant",
},
initLocalState: (localRecord, source, environment) => {
if (params.initLocalState) {
params.initLocalState(localRecord, source, environment);
}
},
});
}
@@ -0,0 +1,59 @@
// Enable after this is solved: https://github.com/projectfluent/fluent.js/issues/280
import React from "react";
import TestRenderer from "react-test-renderer";
import { RecordProxy } from "relay-runtime";
import AppContainer from "talk-auth/containers/AppContainer";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import createEnvironment from "./createEnvironment";
const environment = createEnvironment({
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue("SIGN_IN", "view");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
<TalkContextProvider value={context}>
<AppContainer />
</TalkContextProvider>
);
it("renders sign in form", async () => {
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("navigates to sign up form", async () => {
testRenderer.root
.findByProps({ id: "signIn-gotoSignUpButton" })
.props.onClick();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("navigates to sign in form", async () => {
testRenderer.root
.findByProps({ id: "signUp-gotoSignInButton" })
.props.onClick();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("navigates to forgot password form", async () => {
testRenderer.root
.findByProps({ id: "signIn-gotoForgotPasswordButton" })
.props.onClick();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
+140
View File
@@ -0,0 +1,140 @@
import React from "react";
import TestRenderer, { ReactTestInstance } from "react-test-renderer";
import { RecordProxy } from "relay-runtime";
import sinon from "sinon";
import AppContainer from "talk-auth/containers/AppContainer";
import { animationFrame, timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import createEnvironment from "./createEnvironment";
const environment = createEnvironment({
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue("SIGN_IN", "view");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
<TalkContextProvider value={context}>
<AppContainer />
</TalkContextProvider>
);
const inputPredicate = (name: string) => (n: ReactTestInstance) => {
return n.props.name === name && n.props.onChange;
};
const form = testRenderer.root.findByType("form");
it("renders sign in form", async () => {
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("shows error when submitting empty form", async () => {
form.props.onSubmit();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for invalid email", async () => {
form
.find(inputPredicate("email"))
.props.onChange({ target: { value: "invalid-email" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("accepts valid email", async () => {
form
.find(inputPredicate("email"))
.props.onChange({ target: { value: "hans@test.com" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("accepts correct password", async () => {
form
.find(inputPredicate("password"))
.props.onChange({ target: { value: "testtest" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("shows server error", async () => {
const windowMock = sinon.mock(window);
windowMock.expects("resizeTo");
const error = new Error("Server Error");
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/tenant/auth/local", {
method: "POST",
body: {
email: "hans@test.com",
password: "testtest",
},
})
.once()
.throws(error);
const postMessageMock = sinon.mock(context.postMessage);
postMessageMock
.expects("send")
.withArgs("authError", error.toString(), window.opener)
.once();
form.props.onSubmit();
expect(testRenderer.toJSON()).toMatchSnapshot();
// popup resize will be triggered if we wait for the animation frame first.
await animationFrame();
await timeout();
expect(testRenderer.toJSON()).toMatchSnapshot();
restMock.verify();
postMessageMock.verify();
windowMock.verify();
});
it("submits form successfully", async () => {
const windowMock = sinon.mock(window);
windowMock.expects("close").once();
windowMock.expects("resizeTo");
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/tenant/auth/local", {
method: "POST",
body: {
email: "hans@test.com",
password: "testtest",
},
})
.once()
.returns({ token: "auth-token" });
const postMessageMock = sinon.mock(context.postMessage);
postMessageMock
.expects("send")
.withArgs("setAuthToken", "auth-token", window.opener)
.once();
form.props.onSubmit();
expect(testRenderer.toJSON()).toMatchSnapshot();
// popup resize will be triggered if we wait for the animation frame first.
await animationFrame();
await timeout();
expect(testRenderer.toJSON()).toMatchSnapshot();
restMock.verify();
postMessageMock.verify();
windowMock.verify();
});
+191
View File
@@ -0,0 +1,191 @@
import React from "react";
import TestRenderer, { ReactTestInstance } from "react-test-renderer";
import { RecordProxy } from "relay-runtime";
import sinon from "sinon";
import AppContainer from "talk-auth/containers/AppContainer";
import { animationFrame, timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import createEnvironment from "./createEnvironment";
const environment = createEnvironment({
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue("SIGN_UP", "view");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
<TalkContextProvider value={context}>
<AppContainer />
</TalkContextProvider>
);
const inputPredicate = (name: string) => (n: ReactTestInstance) => {
return n.props.name === name && n.props.onChange;
};
const form = testRenderer.root.findByType("form");
it("renders sign up form", async () => {
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("shows error when submitting empty form", async () => {
form.props.onSubmit();
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for invalid email", async () => {
form
.find(inputPredicate("email"))
.props.onChange({ target: { value: "invalid-email" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("accepts valid email", async () => {
form
.find(inputPredicate("email"))
.props.onChange({ target: { value: "hans@test.com" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for too short username", async () => {
form
.find(inputPredicate("username"))
.props.onChange({ target: { value: "u" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for too long username", async () => {
form
.find(inputPredicate("username"))
.props.onChange({ target: { value: "a".repeat(100) } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for invalid characters in username", async () => {
form
.find(inputPredicate("username"))
.props.onChange({ target: { value: "$%$§$%$§%" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("accepts valid username", async () => {
form
.find(inputPredicate("username"))
.props.onChange({ target: { value: "hans" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for too short password", async () => {
form
.find(inputPredicate("password"))
.props.onChange({ target: { value: "pass" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("accepts correct password", async () => {
form
.find(inputPredicate("password"))
.props.onChange({ target: { value: "testtest" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("checks for wrong password confirmation", async () => {
form
.find(inputPredicate("confirmPassword"))
.props.onChange({ target: { value: "not-matching" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("accepts correct password confirmation", async () => {
form
.find(inputPredicate("confirmPassword"))
.props.onChange({ target: { value: "testtest" } });
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("shows server error", async () => {
const windowMock = sinon.mock(window);
windowMock.expects("resizeTo");
const error = new Error("Server Error");
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/tenant/auth/local/signup", {
method: "POST",
body: {
username: "hans",
email: "hans@test.com",
password: "testtest",
},
})
.once()
.throws(error);
const postMessageMock = sinon.mock(context.postMessage);
postMessageMock
.expects("send")
.withArgs("authError", error.toString(), window.opener)
.once();
form.props.onSubmit();
expect(testRenderer.toJSON()).toMatchSnapshot();
// popup resize will be triggered if we wait for the animation frame first.
await animationFrame();
await timeout();
expect(testRenderer.toJSON()).toMatchSnapshot();
restMock.verify();
postMessageMock.verify();
windowMock.verify();
});
it("submits form successfully", async () => {
const windowMock = sinon.mock(window);
windowMock.expects("close").once();
windowMock.expects("resizeTo");
const restMock = sinon.mock(context.rest);
restMock
.expects("fetch")
.withArgs("/tenant/auth/local/signup", {
method: "POST",
body: {
username: "hans",
email: "hans@test.com",
password: "testtest",
},
})
.once()
.returns({ token: "auth-token" });
const postMessageMock = sinon.mock(context.postMessage);
postMessageMock
.expects("send")
.withArgs("setAuthToken", "auth-token", window.opener)
.once();
form.props.onSubmit();
expect(testRenderer.toJSON()).toMatchSnapshot();
// popup resize will be triggered if we wait for the animation frame first.
await animationFrame();
await timeout();
expect(testRenderer.toJSON()).toMatchSnapshot();
restMock.verify();
postMessageMock.verify();
windowMock.verify();
});
@@ -1,5 +1,5 @@
import { LocalizationProvider } from "fluent-react/compat";
import { MessageContext } from "fluent/compat";
import { FluentBundle } from "fluent/compat";
import { Child as PymChild } from "pym.js";
import React, { StatelessComponent } from "react";
import { MediaQueryMatchers } from "react-responsive";
@@ -15,8 +15,8 @@ export interface TalkContext {
/** relayEnvironment for our relay framework. */
relayEnvironment: Environment;
/** localMessages for our i18n framework. */
localeMessages: MessageContext[];
/** localeBundles for our i18n framework. */
localeBundles: FluentBundle[];
/** formatter for timeago. */
timeagoFormatter?: Formatter;
@@ -61,7 +61,7 @@ export const TalkContextProvider: StatelessComponent<{
value: TalkContext;
}> = ({ value, children }) => (
<Provider value={value}>
<LocalizationProvider messages={value.localeMessages}>
<LocalizationProvider bundles={value.localeBundles}>
<UIContext.Provider
value={{
timeagoFormatter: value.timeagoFormatter,
@@ -14,7 +14,7 @@ import {
import { RestClient } from "talk-framework/lib/rest";
import { ClickFarAwayRegister } from "talk-ui/components/ClickOutside";
import { generateMessages, LocalesData, negotiateLanguages } from "../i18n";
import { generateBundles, LocalesData, negotiateLanguages } from "../i18n";
import { createFetch, TokenGetter } from "../network";
import { PostMessageService } from "../postMessage";
import { TalkContext } from "./TalkContext";
@@ -104,17 +104,17 @@ export default async function createContext({
console.log(`Negotiated locales ${JSON.stringify(locales)}`);
}
const localeMessages = await generateMessages(locales, localesData);
const localeBundles = await generateBundles(locales, localesData);
// Assemble context.
const context = {
relayEnvironment,
localeMessages,
localeBundles,
timeagoFormatter,
pym,
eventEmitter,
registerClickFarAway,
rest: new RestClient("/api"),
rest: new RestClient("/api", tokenGetter),
postMessage: new PostMessageService(),
localStorage: createLocalStorage(),
sessionStorage: createSessionStorage(),
@@ -1,12 +1,12 @@
import { mapValues, once } from "lodash";
import { ReactNode } from "react";
import { VALIDATION_REQUIRED, VALIDATION_TOO_SHORT } from "../messages";
import { VALIDATION_REQUIRED } from "../messages";
/**
* ValidationError represents all possible string values
* that is responded by the server.
*/
type ValidationError = "TOO_SHORT";
type ValidationError = "REQUIRED";
/**
* InvalidArgsMap as responded by the server.
@@ -36,7 +36,6 @@ interface BadUserInputExtension {
* Map server `ValidationError` to a translation message.
*/
const validationMap = {
TOO_SHORT: VALIDATION_TOO_SHORT,
REQUIRED: VALIDATION_REQUIRED,
};
@@ -2,4 +2,5 @@ export { default as NetworkError } from "./networkError";
export { default as UnknownServerError } from "./unknownServerError";
export { default as BadUserInputError } from "./badUserInputError";
export { default as GraphQLError } from "./graphqlError";
export * from "./graphqlError";
+17 -15
View File
@@ -1,6 +1,6 @@
import "fluent-intl-polyfill/compat";
import { negotiateLanguages as negotiate } from "fluent-langneg/compat";
import { MessageContext } from "fluent/compat";
import { FluentBundle } from "fluent/compat";
export interface BundledLocales {
[locale: string]: string;
@@ -47,18 +47,20 @@ export function negotiateLanguages(
}
// Don't warn in production.
let decorateWarnMissing = (cx: MessageContext) => cx;
let decorateWarnMissing = (bundle: FluentBundle) => bundle;
// Warn about missing locales if we are not in production.
if (process.env.NODE_ENV !== "production") {
decorateWarnMissing = (() => {
const warnings: string[] = [];
return (cx: MessageContext) => {
const original = cx.hasMessage;
cx.hasMessage = (id: string) => {
const result = original.apply(cx, [id]);
return (bundle: FluentBundle) => {
const original = bundle.hasMessage;
bundle.hasMessage = (id: string) => {
const result = original.apply(bundle, [id]);
if (!result) {
const warn = `${cx.locales} translation for key "${id}" not found`;
const warn = `${
bundle.locales
} translation for key "${id}" not found`;
if (!warnings.includes(warn)) {
// tslint:disable:next-line: no-console
console.warn(warn);
@@ -67,7 +69,7 @@ if (process.env.NODE_ENV !== "production") {
}
return result;
};
return cx;
return bundle;
};
})();
}
@@ -79,21 +81,21 @@ if (process.env.NODE_ENV !== "production") {
*
* Use it in conjunction with `negotiateLanguages`.
*/
export async function generateMessages(
export async function generateBundles(
locales: ReadonlyArray<string>,
data: LocalesData
): Promise<MessageContext[]> {
): Promise<FluentBundle[]> {
const promises = [];
for (const locale of locales) {
const cx = new MessageContext(locale);
const bundle = new FluentBundle(locale);
if (locale in data.bundled) {
cx.addMessages(data.bundled[locale]);
promises.push(decorateWarnMissing(cx));
bundle.addMessages(data.bundled[locale]);
promises.push(decorateWarnMissing(bundle));
} else if (locale in data.loadables) {
const content = await data.loadables[locale]();
cx.addMessages(content);
promises.push(decorateWarnMissing(cx));
bundle.addMessages(content);
promises.push(decorateWarnMissing(bundle));
} else {
throw Error(`Locale ${locale} not available`);
}
+38 -2
View File
@@ -12,8 +12,44 @@ export const VALIDATION_REQUIRED = () => (
</Localized>
);
export const VALIDATION_TOO_SHORT = () => (
export const VALIDATION_TOO_SHORT = (minLength: number) => (
<Localized id="framework-validation-tooShort">
<span>This field is too short.</span>
<span>{"This field must contain at least {$minLength} characters."}</span>
</Localized>
);
export const INVALID_EMAIL = () => (
<Localized id="framework-validation-invalidEmail">
<span>Please enter a valid email address.</span>
</Localized>
);
export const INVALID_CHARACTERS = () => (
<Localized id="framework-validation-invalidCharacters">
<span>Invalid characters. Try again.</span>
</Localized>
);
export const USERNAME_TOO_SHORT = (minLength: number) => (
<Localized id="framework-validation-usernameTooShort" $minLength={minLength}>
<span>{"Usernames must contain at least {$minLength} characters."}</span>
</Localized>
);
export const USERNAME_TOO_LONG = (maxLength: number) => (
<Localized id="framework-validation-usernameTooLong" $maxLength={maxLength}>
<span>{"Usernames cannot be longer than {$maxLength} characters."}</span>
</Localized>
);
export const PASSWORD_TOO_SHORT = (minLength: number) => (
<Localized id="framework-validation-passwordTooShort" $minLength={minLength}>
<span>{"Password must contain at least {$minLength} characters."}</span>
</Localized>
);
export const PASSWORDS_DO_NOT_MATCH = () => (
<Localized id="framework-validation-passwordsDoNotMatch">
<span>Passwords do not match. Try again.</span>
</Localized>
);
+24 -15
View File
@@ -17,22 +17,27 @@ const buildOptions = (inputOptions: RequestInit = {}) => {
return options;
};
const handleResp = (res: Response) => {
if (res.status > 399) {
return res.json().then((err: any) => {
// TODO: sync error handling with server.
const message = err.message || err.error || res.status;
const error = new Error(message);
throw error;
});
} else if (res.status === 204) {
return res.text();
} else {
return res.json();
// TODO (bc): Wrap response errors into error objects once server errors have been defined
const handleResp = async (res: Response) => {
if (res.status === 404) {
const response = await res.text();
throw new Error(response);
}
if (!res.ok) {
const response = await res.json();
throw new Error(response.error);
}
if (res.status === 204) {
return res.text();
}
return res.json();
};
type PartialRequestInit = Overwrite<Partial<RequestInit>, { body: any }>;
type PartialRequestInit = Overwrite<Partial<RequestInit>, { body?: any }>;
export class RestClient {
public readonly uri: string;
@@ -43,7 +48,10 @@ export class RestClient {
this.tokenGetter = tokenGetter;
}
public fetch<T>(path: string, options: PartialRequestInit): Promise<T> {
public async fetch<T = {}>(
path: string,
options: PartialRequestInit
): Promise<T> {
let opts = options;
if (this.tokenGetter) {
opts = merge({}, options, {
@@ -52,6 +60,7 @@ export class RestClient {
},
});
}
return fetch(`${this.uri}${path}`, buildOptions(opts)).then(handleResp);
const response = await fetch(`${this.uri}${path}`, buildOptions(opts));
return handleResp(response);
}
}
+66 -1
View File
@@ -1,5 +1,13 @@
import { ReactNode } from "react";
import { VALIDATION_REQUIRED } from "./messages";
import {
INVALID_CHARACTERS,
INVALID_EMAIL,
PASSWORD_TOO_SHORT,
PASSWORDS_DO_NOT_MATCH,
USERNAME_TOO_LONG,
USERNAME_TOO_SHORT,
VALIDATION_REQUIRED,
} from "./messages";
type Validator<T, V> = (v: T, values: V) => ReactNode;
@@ -31,3 +39,60 @@ export function composeValidators<T = any, V = any>(
* required is a Validator that checks that the value is truthy.
*/
export const required = createValidator(v => !!v, VALIDATION_REQUIRED());
/**
* validateEmail is a Validator that checks that the value is an email.
*/
export const validateEmail = createValidator(
v => /^.+@.+\..+$/.test(v),
INVALID_EMAIL()
);
/**
* validateUsernameCharacters is a Validator that checks that the username only contains valid characters.
*/
export const validateUsernameCharacters = createValidator(
v => /^[a-zA-Z0-9_.]+$/.test(v),
INVALID_CHARACTERS()
);
/**
* validateUsernameMinLength is a Validator that checks that the username has a min length of characters
*/
export const validateUsernameMinLength = createValidator(
v => v.length >= 3,
USERNAME_TOO_SHORT(3)
);
/**
* validateUsernameMaxLength is a Validator that checks that the username has a max length of characters
*/
export const validateUsernameMaxLength = createValidator(
v => v.length <= 20,
USERNAME_TOO_LONG(20)
);
/**
* validateUsername is a Validator that checks that the username is valid.
*/
export const validateUsername = composeValidators(
validateUsernameCharacters,
validateUsernameMinLength,
validateUsernameMaxLength
);
/**
* validateUsername is a Validator that checks that the value is a valid username.
*/
export const validatePassword = createValidator(
v => v.length >= 8,
PASSWORD_TOO_SHORT(8)
);
/**s
* validateUsername is a Validator that checks that the value is a valid username.
*/
export const validateEqualPasswords = createValidator(
(v, values) => v === values.password,
PASSWORDS_DO_NOT_MATCH()
);
@@ -23,6 +23,8 @@ export async function commit(
} else {
localStorage.removeItem("authToken");
}
// Increment auth revision to indicate a change in auth state.
record.setValue(record.getValue("authRevision") + 1, "authRevision");
// Force gc to trigger.
environment
@@ -0,0 +1,18 @@
import { Environment } from "relay-runtime";
import { TalkContext } from "talk-framework/lib/bootstrap";
import { createMutationContainer } from "talk-framework/lib/relay";
import signOut from "../rest/signOut";
import { commit as setAuthToken } from "./SetAuthTokenMutation";
export type SignOutMutation = () => Promise<void>;
export async function commit(
environment: Environment,
input: undefined,
ctx: TalkContext
) {
await signOut(ctx.rest);
await setAuthToken(environment, { authToken: "" }, ctx);
}
export const withSignOutMutation = createMutationContainer("signOut", commit);
@@ -3,3 +3,4 @@ export {
SetAuthTokenMutation,
SetAuthTokenInput,
} from "./SetAuthTokenMutation";
export { withSignOutMutation, SignOutMutation } from "./SignOutMutation";
+1
View File
@@ -1,2 +1,3 @@
export { default as signIn, SignInInput } from "./signIn";
export { default as signUp, SignUpInput } from "./signUp";
export { default as signOut } from "./signOut";
@@ -0,0 +1,7 @@
import { RestClient } from "../lib/rest";
export default function signOut(rest: RestClient) {
return rest.fetch("/tenant/auth", {
method: "DELETE",
});
}
@@ -2,7 +2,6 @@ import * as React from "react";
import { StatelessComponent } from "react";
import { Flex } from "talk-ui/components";
import PermalinkViewQuery from "../queries/PermalinkViewQuery";
import StreamQuery from "../queries/StreamQuery";
@@ -1,5 +1,4 @@
.root {
width: 100%;
}
.footer {
margin-top: var(--spacing-unit);
@@ -16,7 +16,7 @@ const Username: StatelessComponent<UsernameProps> = props => {
<Typography
variant={matches ? "heading2" : "heading3"}
className={styles.root}
component="span"
container="span"
>
{props.children}
</Typography>
@@ -2,28 +2,20 @@
exports[`renders correctly on big screens 1`] = `
<div
className="Flex-root TopBar-root"
className="Flex-root TopBar-root Flex-flex Flex-itemGutter Flex-alignBaseline Flex-directionRow"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignBaseline Flex-directionRow"
>
<div>
Hello World
</div>
<div>
Hello World
</div>
</div>
`;
exports[`renders correctly on small screens 1`] = `
<div
className="Flex-root TopBar-root"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<div>
Hello World
</div>
<div>
Hello World
</div>
</div>
`;
@@ -1,5 +1,4 @@
.root {
width: 100%;
}
.textField {
@@ -1,7 +1,7 @@
import { Localized } from "fluent-react/compat";
import React, { MouseEvent, StatelessComponent } from "react";
import { Button, Flex, Typography } from "talk-ui/components";
import { Button, Typography } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
import * as styles from "./PermalinkView.css";
@@ -41,11 +41,7 @@ const PermalinkView: StatelessComponent<PermalinkViewProps> = ({
<Typography>Comment not found</Typography>
</Localized>
)}
{comment && (
<Flex direction="column">
<CommentContainer data={comment} />
</Flex>
)}
{comment && <CommentContainer data={comment} />}
</div>
);
};
@@ -1,18 +1,17 @@
.root {
width: 100%;
}
.textarea {
composes: bodyCopy from "talk-ui/shared/typography.css";
display: block;
height: 100px;
height: 150px;
width: 100%;
box-sizing: border-box;
margin-bottom: var(--spacing-unit);
padding: var(--spacing-unit);
border-radius: var(--round-corners);
resize: vertical;
}
.postButtonContainer {
display: flex;
justify-content: flex-end;
.poweredBy {
margin-top: calc(-0.5 * var(--spacing-unit));
}
@@ -1,13 +1,11 @@
import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import React, { StatelessComponent } from "react";
import { Field, Form } from "react-final-form";
import { OnSubmit } from "talk-framework/lib/form";
import { required } from "talk-framework/lib/validation";
import { Button, Typography } from "talk-ui/components";
import { Button, Flex, HorizontalGutter, Typography } from "talk-ui/components";
import * as styles from "./PostCommentForm.css";
import PoweredBy from "./PoweredBy";
interface FormProps {
body: string;
@@ -21,31 +19,44 @@ const PostCommentForm: StatelessComponent<PostCommentFormProps> = props => (
<Form onSubmit={props.onSubmit}>
{({ handleSubmit, submitting }) => (
<form autoComplete="off" onSubmit={handleSubmit} className={styles.root}>
<Field name="body" validate={required}>
{({ input, meta }) => (
<div>
<textarea
className={styles.textarea}
name={input.name}
onChange={input.onChange}
value={input.value}
/>
{meta.touched &&
(meta.error || meta.submitError) && (
<Typography align="right" color="error" gutterBottom>
{meta.error || meta.submitError}
</Typography>
)}
</div>
)}
</Field>
<div className={styles.postButtonContainer}>
<Localized id="comments-postCommentForm-post">
<Button color="primary" variant="filled" disabled={submitting}>
Post
</Button>
</Localized>
</div>
<HorizontalGutter>
<Field name="body" validate={required}>
{({ input, meta }) => (
<div>
<textarea
className={styles.textarea}
name={input.name}
onChange={input.onChange}
value={input.value}
placeholder="Post a comment"
/>
{meta.touched &&
(meta.error || meta.submitError) && (
<Typography align="right" color="error" gutterBottom>
{meta.error || meta.submitError}
</Typography>
)}
</div>
)}
</Field>
<Flex
direction="row"
justifyContent="space-between"
alignItems="flex-start"
>
<PoweredBy className={styles.poweredBy} />
<Localized id="comments-postCommentForm-submit">
<Button
color="primary"
variant="filled"
disabled={submitting}
type="submit"
>
Submit
</Button>
</Localized>
</Flex>
</HorizontalGutter>
</form>
)}
</Form>
@@ -0,0 +1,13 @@
.root {
}
.textarea {
composes: bodyCopy from "talk-ui/shared/typography.css";
display: block;
height: 150px;
width: 100%;
box-sizing: border-box;
padding: var(--spacing-unit);
border-radius: var(--round-corners);
resize: vertical;
}
@@ -0,0 +1,9 @@
import { shallow } from "enzyme";
import React from "react";
import PostCommentFormFake from "./PostCommentFormFake";
it("renders correctly", () => {
const wrapper = shallow(<PostCommentFormFake />);
expect(wrapper).toMatchSnapshot();
});
@@ -0,0 +1,21 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { Button, HorizontalGutter } from "talk-ui/components";
import * as styles from "./PostCommentFormFake.css";
const PostCommentFormFake: StatelessComponent = props => (
<HorizontalGutter className={styles.root}>
<textarea
className={styles.textarea}
placeholder="Post a comment"
disabled
/>
<Localized id="comments-PostCommentFormFake-signInAndJoin">
<Button color="primary" variant="filled" disabled type="submit" fullWidth>
Sign in and join the conversation
</Button>
</Localized>
</HorizontalGutter>
);
export default PostCommentFormFake;
@@ -0,0 +1,14 @@
import { shallow } from "enzyme";
import React from "react";
import { PropTypesOf } from "talk-framework/types";
import PoweredBy from "./PoweredBy";
it("renders correctly", () => {
const props: PropTypesOf<typeof PoweredBy> = {
className: "custom",
};
const wrapper = shallow(<PoweredBy {...props} />);
expect(wrapper).toMatchSnapshot();
});
@@ -0,0 +1,26 @@
import cn from "classnames";
import React, { StatelessComponent } from "react";
import { Typography } from "talk-ui/components";
interface Props {
className: string;
}
const PoweredBy: StatelessComponent<Props> = props => {
return (
<div className={cn(props.className)}>
<Typography
variant="inputDescription"
container="span"
color="textSecondary"
>
Powered by
</Typography>{" "}
<Typography variant="heading4" container="span">
The Coral Project
</Typography>
</div>
);
};
export default PoweredBy;
@@ -2,7 +2,7 @@ import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import { Button, Flex } from "talk-ui/components";
import { Button, HorizontalGutter } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
import Indent from "./Indent";
@@ -18,11 +18,9 @@ export interface ReplyListProps {
const ReplyList: StatelessComponent<ReplyListProps> = props => {
return (
<Indent>
<Flex
direction="column"
<HorizontalGutter
id={`talk-comments-replyList-log--${props.commentID}`}
role="log"
itemGutter
>
{props.comments.map(comment => (
<CommentContainer key={comment.id} data={comment} />
@@ -41,7 +39,7 @@ const ReplyList: StatelessComponent<ReplyListProps> = props => {
</Button>
</Localized>
)}
</Flex>
</HorizontalGutter>
</Indent>
);
};
+17 -11
View File
@@ -2,12 +2,13 @@ import { Localized } from "fluent-react/compat";
import * as React from "react";
import { StatelessComponent } from "react";
import { Button, Flex } from "talk-ui/components";
import { Button, HorizontalGutter } from "talk-ui/components";
import CommentContainer from "../containers/CommentContainer";
import PostCommentFormContainer from "../containers/PostCommentFormContainer";
import ReplyListContainer from "../containers/ReplyListContainer";
import UserBoxContainer from "../containers/UserBoxContainer";
import PostCommentFormFake from "./PostCommentFormFake";
import * as styles from "./Stream.css";
export interface StreamProps {
@@ -17,25 +18,30 @@ export interface StreamProps {
onLoadMore?: () => void;
hasMore?: boolean;
disableLoadMore?: boolean;
user: {} | null;
}
const Stream: StatelessComponent<StreamProps> = props => {
return (
<Flex className={styles.root} direction="column" itemGutter>
<UserBoxContainer />
<PostCommentFormContainer assetID={props.assetID} />
<Flex
direction="column"
<HorizontalGutter className={styles.root} size="double">
<HorizontalGutter size="half">
<UserBoxContainer user={props.user} />
{props.user ? (
<PostCommentFormContainer assetID={props.assetID} />
) : (
<PostCommentFormFake />
)}
</HorizontalGutter>
<HorizontalGutter
id="talk-comments-stream-log"
role="log"
aria-live="polite"
itemGutter
>
{props.comments.map(comment => (
<Flex direction="column" key={comment.id} itemGutter>
<HorizontalGutter key={comment.id}>
<CommentContainer data={comment} />
<ReplyListContainer comment={comment} />
</Flex>
</HorizontalGutter>
))}
{props.hasMore && (
<Localized id="comments-stream-loadMore">
@@ -51,8 +57,8 @@ const Stream: StatelessComponent<StreamProps> = props => {
</Button>
</Localized>
)}
</Flex>
</Flex>
</HorizontalGutter>
</HorizontalGutter>
);
};
@@ -0,0 +1,3 @@
.child {
width: initial;
}
@@ -0,0 +1,16 @@
import { shallow } from "enzyme";
import { noop } from "lodash";
import React from "react";
import { PropTypesOf } from "talk-framework/types";
import UserBoxAuthenticated from "./UserBoxAuthenticated";
it("renders correctly", () => {
const props: PropTypesOf<typeof UserBoxAuthenticated> = {
onSignOut: noop,
username: "Username",
};
const wrapper = shallow(<UserBoxAuthenticated {...props} />);
expect(wrapper).toMatchSnapshot();
});
@@ -0,0 +1,49 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import { Button, Flex, Typography } from "talk-ui/components";
export interface UserBoxAuthenticatedProps {
onSignOut: () => void;
username: string;
}
const UserBoxAuthenticated: StatelessComponent<
UserBoxAuthenticatedProps
> = props => {
const Username = () => (
<Typography variant="bodyCopyBold" container="span">
{props.username}
</Typography>
);
return (
<Flex itemGutter="half" wrap>
<Localized
id="comments-userBoxAuthenticated-signedInAs"
username={<Username />}
>
<Typography variant="bodyCopy" container="div">
{"Signed in as <username></username>."}
</Typography>
</Localized>
<Localized
id="comments-userBoxAuthenticated-notYou"
button={
<Button
color="primary"
size="small"
variant="underlined"
onClick={props.onSignOut}
/>
}
>
<Typography variant="bodyCopy" container={Flex}>
{"Not you? <button>Sign Out</button>"}
</Typography>
</Localized>
</Flex>
);
};
export default UserBoxAuthenticated;
@@ -15,23 +15,28 @@ const UserBoxUnauthenticated: StatelessComponent<
UserBoxUnauthenticatedProps
> = props => {
return (
<Flex>
<Flex itemGutter alignItems="center">
<MatchMedia gteWidth="sm">
<Localized id="comments-userBoxUnauthenticated-joinTheConversation">
<Typography
className={styles.joinText}
variant="bodyCopyBold"
component="span"
container="span"
>
Join the conversation
</Typography>
</Localized>
<Typography variant="bodyCopyBold" component="span">
<Typography variant="bodyCopyBold" container="span">
|
</Typography>
</MatchMedia>
<Localized id="comments-userBoxUnauthenticated-signIn">
<Button color="primary" size="small" onClick={props.onSignIn}>
<Button
color="primary"
size="small"
variant="underlined"
onClick={props.onSignIn}
>
Sign in
</Button>
</Localized>
@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<withPropsOnChange(HorizontalGutter)
className="PostCommentFormFake-root"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<Localized
id="comments-PostCommentFormFake-signInAndJoin"
>
<withPropsOnChange(Button)
color="primary"
disabled={true}
fullWidth={true}
type="submit"
variant="filled"
>
Sign in and join the conversation
</withPropsOnChange(Button)>
</Localized>
</withPropsOnChange(HorizontalGutter)>
`;
@@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<div
className="custom"
>
<withPropsOnChange(Typography)
color="textSecondary"
container="span"
variant="inputDescription"
>
Powered by
</withPropsOnChange(Typography)>
<withPropsOnChange(Typography)
container="span"
variant="heading4"
>
The Coral Project
</withPropsOnChange(Typography)>
</div>
`;
@@ -2,10 +2,8 @@
exports[`renders correctly 1`] = `
<Indent>
<withPropsOnChange(Flex)
direction="column"
<withPropsOnChange(HorizontalGutter)
id="talk-comments-replyList-log--comment-id"
itemGutter={true}
role="log"
>
<Relay(CommentContainer)
@@ -24,16 +22,14 @@ exports[`renders correctly 1`] = `
}
key="comment-2"
/>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
</Indent>
`;
exports[`when there is more disables load more button 1`] = `
<Indent>
<withPropsOnChange(Flex)
direction="column"
<withPropsOnChange(HorizontalGutter)
id="talk-comments-replyList-log--comment-id"
itemGutter={true}
role="log"
>
<Relay(CommentContainer)
@@ -66,16 +62,14 @@ exports[`when there is more disables load more button 1`] = `
Show All Replies
</withPropsOnChange(Button)>
</Localized>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
</Indent>
`;
exports[`when there is more renders a load more button 1`] = `
<Indent>
<withPropsOnChange(Flex)
direction="column"
<withPropsOnChange(HorizontalGutter)
id="talk-comments-replyList-log--comment-id"
itemGutter={true}
role="log"
>
<Relay(CommentContainer)
@@ -108,6 +102,6 @@ exports[`when there is more renders a load more button 1`] = `
Show All Replies
</withPropsOnChange(Button)>
</Localized>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
</Indent>
`;
@@ -1,25 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<withPropsOnChange(Flex)
<withPropsOnChange(HorizontalGutter)
className="Stream-root"
direction="column"
itemGutter={true}
size="double"
>
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(UserBoxContainer)))))) />
<withContext(createMutationContainer(PostCommentFormContainer))
assetID="asset-id"
/>
<withPropsOnChange(Flex)
<withPropsOnChange(HorizontalGutter)
size="half"
>
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(Relay(UserBoxContainer))))))))) />
<PostCommentFormFake />
</withPropsOnChange(HorizontalGutter)>
<withPropsOnChange(HorizontalGutter)
aria-live="polite"
direction="column"
id="talk-comments-stream-log"
itemGutter={true}
role="log"
>
<withPropsOnChange(Flex)
direction="column"
itemGutter={true}
<withPropsOnChange(HorizontalGutter)
key="comment-1"
>
<Relay(CommentContainer)
@@ -36,10 +33,8 @@ exports[`renders correctly 1`] = `
}
}
/>
</withPropsOnChange(Flex)>
<withPropsOnChange(Flex)
direction="column"
itemGutter={true}
</withPropsOnChange(HorizontalGutter)>
<withPropsOnChange(HorizontalGutter)
key="comment-2"
>
<Relay(CommentContainer)
@@ -56,31 +51,28 @@ exports[`renders correctly 1`] = `
}
}
/>
</withPropsOnChange(Flex)>
</withPropsOnChange(Flex)>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
</withPropsOnChange(HorizontalGutter)>
</withPropsOnChange(HorizontalGutter)>
`;
exports[`when there is more disables load more button 1`] = `
<withPropsOnChange(Flex)
<withPropsOnChange(HorizontalGutter)
className="Stream-root"
direction="column"
itemGutter={true}
size="double"
>
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(UserBoxContainer)))))) />
<withContext(createMutationContainer(PostCommentFormContainer))
assetID="asset-id"
/>
<withPropsOnChange(Flex)
<withPropsOnChange(HorizontalGutter)
size="half"
>
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(Relay(UserBoxContainer))))))))) />
<PostCommentFormFake />
</withPropsOnChange(HorizontalGutter)>
<withPropsOnChange(HorizontalGutter)
aria-live="polite"
direction="column"
id="talk-comments-stream-log"
itemGutter={true}
role="log"
>
<withPropsOnChange(Flex)
direction="column"
itemGutter={true}
<withPropsOnChange(HorizontalGutter)
key="comment-1"
>
<Relay(CommentContainer)
@@ -97,10 +89,8 @@ exports[`when there is more disables load more button 1`] = `
}
}
/>
</withPropsOnChange(Flex)>
<withPropsOnChange(Flex)
direction="column"
itemGutter={true}
</withPropsOnChange(HorizontalGutter)>
<withPropsOnChange(HorizontalGutter)
key="comment-2"
>
<Relay(CommentContainer)
@@ -117,7 +107,7 @@ exports[`when there is more disables load more button 1`] = `
}
}
/>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
<Localized
id="comments-stream-loadMore"
>
@@ -132,30 +122,27 @@ exports[`when there is more disables load more button 1`] = `
Load More
</withPropsOnChange(Button)>
</Localized>
</withPropsOnChange(Flex)>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
</withPropsOnChange(HorizontalGutter)>
`;
exports[`when there is more renders a load more button 1`] = `
<withPropsOnChange(Flex)
<withPropsOnChange(HorizontalGutter)
className="Stream-root"
direction="column"
itemGutter={true}
size="double"
>
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(UserBoxContainer)))))) />
<withContext(createMutationContainer(PostCommentFormContainer))
assetID="asset-id"
/>
<withPropsOnChange(Flex)
<withPropsOnChange(HorizontalGutter)
size="half"
>
<withContext(createMutationContainer(withContext(createMutationContainer(withContext(createMutationContainer(withContext(withLocalStateContainer(Relay(UserBoxContainer))))))))) />
<PostCommentFormFake />
</withPropsOnChange(HorizontalGutter)>
<withPropsOnChange(HorizontalGutter)
aria-live="polite"
direction="column"
id="talk-comments-stream-log"
itemGutter={true}
role="log"
>
<withPropsOnChange(Flex)
direction="column"
itemGutter={true}
<withPropsOnChange(HorizontalGutter)
key="comment-1"
>
<Relay(CommentContainer)
@@ -172,10 +159,8 @@ exports[`when there is more renders a load more button 1`] = `
}
}
/>
</withPropsOnChange(Flex)>
<withPropsOnChange(Flex)
direction="column"
itemGutter={true}
</withPropsOnChange(HorizontalGutter)>
<withPropsOnChange(HorizontalGutter)
key="comment-2"
>
<Relay(CommentContainer)
@@ -192,7 +177,7 @@ exports[`when there is more renders a load more button 1`] = `
}
}
/>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
<Localized
id="comments-stream-loadMore"
>
@@ -207,6 +192,6 @@ exports[`when there is more renders a load more button 1`] = `
Load More
</withPropsOnChange(Button)>
</Localized>
</withPropsOnChange(Flex)>
</withPropsOnChange(Flex)>
</withPropsOnChange(HorizontalGutter)>
</withPropsOnChange(HorizontalGutter)>
`;
@@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<withPropsOnChange(Flex)
itemGutter="half"
wrap={true}
>
<Localized
id="comments-userBoxAuthenticated-signedInAs"
username={<Username />}
>
<withPropsOnChange(Typography)
container="div"
variant="bodyCopy"
>
Signed in as &lt;username&gt;&lt;/username&gt;.
</withPropsOnChange(Typography)>
</Localized>
<Localized
button={
<withPropsOnChange(Button)
color="primary"
onClick={[Function]}
size="small"
variant="underlined"
/>
}
id="comments-userBoxAuthenticated-notYou"
>
<withPropsOnChange(Typography)
container={[Function]}
variant="bodyCopy"
>
Not you? &lt;button&gt;Sign Out&lt;/button&gt;
</withPropsOnChange(Typography)>
</Localized>
</withPropsOnChange(Flex)>
`;
@@ -1,7 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<withPropsOnChange(Flex)>
<withPropsOnChange(Flex)
alignItems="center"
itemGutter={true}
>
<MatchMediaWithContext
gteWidth="sm"
>
@@ -10,14 +13,14 @@ exports[`renders correctly 1`] = `
>
<withPropsOnChange(Typography)
className="UserBoxUnauthenticated-joinText"
component="span"
container="span"
variant="bodyCopyBold"
>
Join the conversation
</withPropsOnChange(Typography)>
</Localized>
<withPropsOnChange(Typography)
component="span"
container="span"
variant="bodyCopyBold"
>
|
@@ -30,6 +33,7 @@ exports[`renders correctly 1`] = `
color="primary"
onClick={[Function]}
size="small"
variant="underlined"
>
Sign in
</withPropsOnChange(Button)>
@@ -3,7 +3,8 @@ import { graphql, RelayPaginationProp } from "react-relay";
import { withPaginationContainer } from "talk-framework/lib/relay";
import { PropTypesOf } from "talk-framework/types";
import { StreamContainer_asset as Data } from "talk-stream/__generated__/StreamContainer_asset.graphql";
import { StreamContainer_asset as AssetData } from "talk-stream/__generated__/StreamContainer_asset.graphql";
import { StreamContainer_user as UserData } from "talk-stream/__generated__/StreamContainer_user.graphql";
import {
COMMENT_SORT,
StreamContainerPaginationQueryVariables,
@@ -12,7 +13,8 @@ import {
import Stream from "../components/Stream";
interface InnerProps {
asset: Data;
asset: AssetData;
user: UserData | null;
relay: RelayPaginationProp;
}
@@ -31,6 +33,7 @@ export class StreamContainer extends React.Component<InnerProps> {
onLoadMore={this.loadMore}
hasMore={this.props.relay.hasMore()}
disableLoadMore={this.state.disableLoadMore}
user={this.props.user}
/>
);
}
@@ -61,7 +64,7 @@ interface FragmentVariables {
}
const enhanced = withPaginationContainer<
{ asset: Data },
{ asset: AssetData; user: UserData | null },
InnerProps,
FragmentVariables,
StreamContainerPaginationQueryVariables
@@ -88,6 +91,11 @@ const enhanced = withPaginationContainer<
}
}
`,
user: graphql`
fragment StreamContainer_user on User {
...UserBoxContainer_user
}
`,
},
{
direction: "forward",
@@ -14,10 +14,13 @@ it("renders correctly", () => {
view: "SIGN_IN",
},
},
user: null,
// tslint:disable-next-line:no-empty
showAuthPopup: async () => {},
// tslint:disable-next-line:no-empty
setAuthPopupState: async () => {},
// tslint:disable-next-line:no-empty
signOut: async () => {},
};
const wrapper = shallow(<UserBoxContainer {...props} />);
expect(wrapper).toMatchSnapshot();
@@ -1,8 +1,15 @@
import * as React from "react";
import { Component } from "react";
import { graphql, withLocalStateContainer } from "talk-framework/lib/relay";
import {
graphql,
withFragmentContainer,
withLocalStateContainer,
} from "talk-framework/lib/relay";
import { SignOutMutation, withSignOutMutation } from "talk-framework/mutations";
import { UserBoxContainer_user as UserData } from "talk-stream/__generated__/UserBoxContainer_user.graphql";
import { UserBoxContainerLocal as Local } from "talk-stream/__generated__/UserBoxContainerLocal.graphql";
import UserBoxUnauthenticated from "talk-stream/components/UserBoxUnauthenticated";
import {
SetAuthPopupStateMutation,
ShowAuthPopupMutation,
@@ -11,12 +18,14 @@ import {
} from "talk-stream/mutations";
import { Popup } from "talk-ui/components";
import UserBoxUnauthenticated from "../components/UserBoxUnauthenticated";
import UserBoxAuthenticated from "../components/UserBoxAuthenticated";
interface InnerProps {
local: Local;
user: UserData | null;
showAuthPopup: ShowAuthPopupMutation;
setAuthPopupState: SetAuthPopupStateMutation;
signOut: SignOutMutation;
}
export class UserBoxContainer extends Component<InnerProps> {
@@ -31,13 +40,26 @@ export class UserBoxContainer extends Component<InnerProps> {
local: {
authPopup: { open, focus, view },
},
user,
signOut,
} = this.props;
if (user) {
return (
<UserBoxAuthenticated
onSignOut={signOut}
// TODO: why nullable?
username={user.username!}
/>
);
}
return (
<>
<Popup
href={`/auth.html?view=${view}`}
title="Talk Auth"
features="menubar=0,resizable=0,width=500,height=550,top=200,left=500"
features="menubar=0,resizable=0,width=350,height=395,top=200,left=500"
open={open}
focus={focus}
onFocus={this.handleFocus}
@@ -53,19 +75,29 @@ export class UserBoxContainer extends Component<InnerProps> {
}
}
const enhanced = withSetAuthPopupStateMutation(
withShowAuthPopupMutation(
withLocalStateContainer<Local>(
graphql`
fragment UserBoxContainerLocal on Local {
authPopup {
open
focus
view
const enhanced = withSignOutMutation(
withSetAuthPopupStateMutation(
withShowAuthPopupMutation(
withLocalStateContainer<Local>(
graphql`
fragment UserBoxContainerLocal on Local {
authPopup {
open
focus
view
}
}
}
`
)(UserBoxContainer)
`
)(
withFragmentContainer<{ user: UserData | null }>({
user: graphql`
fragment UserBoxContainer_user on User {
username
}
`,
})(UserBoxContainer)
)
)
)
);
@@ -3,7 +3,7 @@
exports[`renders correctly 1`] = `
<React.Fragment>
<Popup
features="menubar=0,resizable=0,width=500,height=550,top=200,left=500"
features="menubar=0,resizable=0,width=350,height=395,top=200,left=500"
focus={false}
href="/auth.html?view=SIGN_IN"
onBlur={[Function]}
+1 -3
View File
@@ -40,9 +40,7 @@ async function main() {
});
const Index: StatelessComponent = () => (
<TalkContextProvider
value={{ ...context, mediaQueryValues: { width: 640 } }}
>
<TalkContextProvider value={context}>
<AppContainer />
</TalkContextProvider>
);
@@ -13,6 +13,7 @@ exports[`init local state 1`] = `
\\"__id\\": \\"client:root.local\\",
\\"__typename\\": \\"Local\\",
\\"authToken\\": \\"\\",
\\"authRevision\\": 0,
\\"network\\": {
\\"__ref\\": \\"client:root.local.network\\"
},
@@ -33,6 +33,9 @@ export default async function initLocalState(
// Set auth token
localRecord.setValue(localStorage.getItem("authToken") || "", "authToken");
// Set initial auth revision, this is increment whenenver auth state might have changed.
localRecord.setValue(0, "authRevision");
// Parse query params
const query = qs.parse(location.search);
@@ -22,6 +22,10 @@ type Local {
commentID: String
authPopup: AuthPopup!
authToken: String
# Used to invalidate the `me` endpoint.
# This is incremented whenever the auth status
# might have changed.
authRevision: Int!
}
extend type Query {
@@ -13,6 +13,7 @@ import {
} from "talk-stream/__generated__/PermalinkViewQuery.graphql";
import { PermalinkViewQueryLocal as Local } from "talk-stream/__generated__/PermalinkViewQueryLocal.graphql";
import { Spinner } from "talk-ui/components";
import PermalinkViewContainer from "../containers/PermalinkViewContainer";
interface InnerProps {
@@ -29,7 +30,7 @@ export const render = ({
if (props) {
return <PermalinkViewContainer comment={props.comment} />;
}
return <div>Loading</div>;
return <Spinner />;
};
const PermalinkViewQuery: StatelessComponent<InnerProps> = ({
+16 -8
View File
@@ -1,7 +1,5 @@
import * as React from "react";
import { StatelessComponent } from "react";
import React, { StatelessComponent } from "react";
import { ReadyState } from "react-relay";
import {
graphql,
QueryRenderer,
@@ -12,7 +10,7 @@ import {
StreamQueryVariables,
} from "talk-stream/__generated__/StreamQuery.graphql";
import { StreamQueryLocal as Local } from "talk-stream/__generated__/StreamQueryLocal.graphql";
import { Spinner } from "talk-ui/components";
import StreamContainer from "../containers/StreamContainer";
interface InnerProps {
@@ -23,25 +21,34 @@ export const render = ({ error, props }: ReadyState<StreamQueryResponse>) => {
if (error) {
return <div>{error.message}</div>;
}
if (props) {
return <StreamContainer asset={props.asset} />;
return <StreamContainer asset={props.asset} user={props.me} />;
}
return <div>Loading</div>;
return <Spinner />;
};
const StreamQuery: StatelessComponent<InnerProps> = ({
local: { assetID },
local: { assetID, authRevision },
}) => (
<QueryRenderer<StreamQueryVariables, StreamQueryResponse>
query={graphql`
query StreamQuery($assetID: ID!) {
query StreamQuery($assetID: ID!, $authRevision: Int!) {
asset(id: $assetID) {
...StreamContainer_asset
}
# authRevision is increment every time auth state has changed.
# This is basically a cache invalidation and causes relay
# to automatically update this query.
me(clientAuthRevision: $authRevision) {
...StreamContainer_user
}
}
`}
variables={{
assetID,
authRevision,
}}
render={render}
/>
@@ -51,6 +58,7 @@ const enhanced = withLocalStateContainer<Local>(
graphql`
fragment StreamQueryLocal on Local {
assetID
authRevision
}
`
)(StreamQuery);
@@ -6,11 +6,7 @@ exports[`renders error 1`] = `
</div>
`;
exports[`renders loading 1`] = `
<div>
Loading
</div>
`;
exports[`renders loading 1`] = `<withPropsOnChange(Spinner) />`;
exports[`renders permalink view container 1`] = `
<withContext(withContext(createMutationContainer(Relay(PermalinkViewContainer))))
@@ -6,11 +6,7 @@ exports[`renders error 1`] = `
</div>
`;
exports[`renders loading 1`] = `
<div>
Loading
</div>
`;
exports[`renders loading 1`] = `<withPropsOnChange(Spinner) />`;
exports[`renders stream container 1`] = `
<Relay(StreamContainer)
@@ -2,212 +2,170 @@
exports[`loads more comments 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
</div>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Flex-root"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
<div
className="Flex-root"
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Isabelle
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:14:00.000Z"
title="2018-07-06T18:14:00.000Z"
>
2018-07-06T18:14:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Hey!
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
2018-07-06T18:20:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Isabelle
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:14:00.000Z"
title="2018-07-06T18:14:00.000Z"
>
2018-07-06T18:14:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Hey!
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -217,188 +175,155 @@ exports[`loads more comments 1`] = `
exports[`renders comment stream 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
</div>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
<button
aria-controls="talk-comments-stream-log"
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantOutlined Button-fullWidth"
disabled={false}
id="talk-comments-stream-loadMore"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Load More
</button>
</div>
className="Comment-footer"
/>
</div>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
<button
aria-controls="talk-comments-stream-log"
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantOutlined Button-fullWidth"
disabled={false}
id="talk-comments-stream-loadMore"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Load More
</button>
</div>
</div>
</div>
@@ -2,70 +2,55 @@
exports[`renders permalink view 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="PermalinkView-root"
>
<div
className="PermalinkView-root"
<a
className="BaseButton-root Button-root PermalinkView-button Button-sizeRegular Button-colorPrimary Button-variantOutlined Button-fullWidth"
href="http://localhost/"
id="talk-comments-permalinkView-showAllComments"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
target="_parent"
type="button"
>
Show all Comments
</a>
<div
className="Comment-root"
role="article"
>
<a
className="BaseButton-root Button-root PermalinkView-button Button-sizeRegular Button-colorPrimary Button-variantOutlined Button-fullWidth"
href="http://localhost/"
id="talk-comments-permalinkView-showAllComments"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
target="_parent"
>
Show all Comments
</a>
<div
className="Flex-root"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Flex-flex Flex-wrap Flex-alignFlexStart Flex-directionColumn"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -73,130 +58,104 @@ exports[`renders permalink view 1`] = `
exports[`show all comments 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
</div>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
</div>
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -2,35 +2,32 @@
exports[`renders permalink view with unknown asset 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="PermalinkView-root"
>
<div
className="PermalinkView-root"
<a
className="BaseButton-root Button-root PermalinkView-button Button-sizeRegular Button-colorPrimary Button-variantOutlined Button-fullWidth"
href="http://localhost/"
id="talk-comments-permalinkView-showAllComments"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
target="_parent"
type="button"
>
<a
className="BaseButton-root Button-root PermalinkView-button Button-sizeRegular Button-colorPrimary Button-variantOutlined Button-fullWidth"
href="http://localhost/"
id="talk-comments-permalinkView-showAllComments"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
target="_parent"
>
Show all Comments
</a>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Comment not found
</p>
</div>
Show all Comments
</a>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Comment not found
</p>
</div>
</div>
`;
@@ -2,165 +2,136 @@
exports[`renders permalink view with unknown comment 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="PermalinkView-root"
>
<div
className="PermalinkView-root"
<a
className="BaseButton-root Button-root PermalinkView-button Button-sizeRegular Button-colorPrimary Button-variantOutlined Button-fullWidth"
href="http://localhost/"
id="talk-comments-permalinkView-showAllComments"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
target="_parent"
type="button"
>
<a
className="BaseButton-root Button-root PermalinkView-button Button-sizeRegular Button-colorPrimary Button-variantOutlined Button-fullWidth"
href="http://localhost/"
id="talk-comments-permalinkView-showAllComments"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
target="_parent"
>
Show all Comments
</a>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Comment not found
</p>
</div>
Show all Comments
</a>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Comment not found
</p>
</div>
</div>
`;
exports[`show all comments 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
</div>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Flex-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
</div>
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -2,249 +2,203 @@
exports[`renders comment stream 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
</div>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
I like yoghurt
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Indent-root Indent-level0"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-replyList-log--comment-with-replies"
role="log"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
I like yoghurt
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Indent-root Indent-level0"
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
<div
className="Flex-root"
id="talk-comments-replyList-log--comment-with-replies"
role="log"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
</div>
2018-07-06T18:20:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -2,171 +2,137 @@
exports[`renders comment stream 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
</div>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
</div>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Flex-root"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
<div
className="Flex-root"
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
2018-07-06T18:20:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -2,191 +2,158 @@
exports[`renders comment stream 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
</div>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
className="Indent-root Indent-level0"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-replyList-log--comment-0"
role="log"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Indent-root Indent-level0"
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
<div
className="Flex-root"
id="talk-comments-replyList-log--comment-0"
role="log"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
<button
aria-controls="talk-comments-replyList-log--comment-0"
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantOutlined Button-fullWidth"
disabled={false}
id="talk-comments-replyList-showAll--comment-0"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Show All Replies
</button>
</div>
</div>
</div>
2018-07-06T18:20:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
<button
aria-controls="talk-comments-replyList-log--comment-0"
className="BaseButton-root Button-root Button-sizeRegular Button-colorRegular Button-variantOutlined Button-fullWidth"
disabled={false}
id="talk-comments-replyList-showAll--comment-0"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Show All Replies
</button>
</div>
</div>
</div>
@@ -197,208 +164,170 @@ exports[`renders comment stream 1`] = `
exports[`show all replies 1`] = `
<div
className="Flex-root App-root"
className="Flex-root App-root Flex-flex Flex-justifyCenter"
>
<div
className="Flex-flex Flex-wrap Flex-justifyCenter Flex-alignCenter"
className="HorizontalGutter-root Stream-root HorizontalGutter-double"
>
<div
className="Flex-root Stream-root"
className="HorizontalGutter-root HorizontalGutter-half"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root Flex-flex Flex-itemGutter Flex-alignCenter"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantUnderlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Register
</button>
</div>
<div
className="HorizontalGutter-root PostCommentFormFake-root HorizontalGutter-full"
>
<textarea
className="PostCommentFormFake-textarea"
disabled={true}
placeholder="Post a comment"
/>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled Button-fullWidth Button-disabled"
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="submit"
>
Sign in and join the conversation
</button>
</div>
</div>
<div
aria-live="polite"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-stream-log"
role="log"
>
<div
className="HorizontalGutter-root HorizontalGutter-full"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-wrap Flex-alignCenter"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Sign in
</button>
<button
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlined"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
Register
</button>
2018-07-06T18:24:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<form
autoComplete="off"
className="PostCommentForm-root"
onSubmit={[Function]}
>
<div>
<textarea
className="PostCommentForm-textarea"
name="body"
onChange={[Function]}
value=""
/>
</div>
<div
className="PostCommentForm-postButtonContainer"
>
<button
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantFilled"
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
>
Post
</button>
</div>
</form>
<div
aria-live="polite"
className="Flex-root"
id="talk-comments-stream-log"
role="log"
className="Indent-root Indent-level0"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="HorizontalGutter-root HorizontalGutter-full"
id="talk-comments-replyList-log--comment-0"
role="log"
>
<div
className="Flex-root"
className="Comment-root"
role="article"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Markus
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:24:00.000Z"
title="2018-07-06T18:24:00.000Z"
>
2018-07-06T18:24:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Joining Too
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Indent-root Indent-level0"
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
<div
className="Flex-root"
id="talk-comments-replyList-log--comment-0"
role="log"
>
<div
className="Flex-flex Flex-itemGutter Flex-wrap Flex-alignFlexStart Flex-directionColumn"
>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Lukas
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:20:00.000Z"
title="2018-07-06T18:20:00.000Z"
>
2018-07-06T18:20:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root"
>
<div
className="Flex-flex Flex-halfItemGutter Flex-wrap Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Isabelle
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:14:00.000Z"
title="2018-07-06T18:14:00.000Z"
>
2018-07-06T18:14:00.000Z
</time>
</div>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Hey!
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
</div>
2018-07-06T18:20:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
What's up?
</p>
<div
className="Comment-footer"
/>
</div>
<div
className="Comment-root"
role="article"
>
<div
className="Flex-root TopBar-root Flex-flex Flex-halfItemGutter Flex-alignBaseline Flex-directionColumn"
>
<span
className="Typography-root Typography-heading3 Typography-colorTextPrimary Username-root"
>
Isabelle
</span>
<time
className="Timestamp-root RelativeTime-root"
dateTime="2018-07-06T18:14:00.000Z"
title="2018-07-06T18:14:00.000Z"
>
2018-07-06T18:14:00.000Z
</time>
</div>
<p
className="Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Hey!
</p>
<div
className="Comment-footer"
/>
</div>
</div>
</div>
@@ -4,6 +4,9 @@ import sinon from "sinon";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -65,13 +68,18 @@ const environment = createEnvironment({
logNetwork: false,
resolvers,
initLocalState: (localRecord, source) => {
localRecord.setValue(0, "authRevision");
localRecord.setValue(assetStub.id, "assetID");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -5,6 +5,9 @@ import sinon from "sinon";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -49,6 +52,7 @@ const environment = createEnvironment({
logNetwork: false,
resolvers,
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(0, "authRevision");
localRecord.setValue(assetStub.id, "assetID");
localRecord.setValue(commentStub.id, "commentID");
},
@@ -56,7 +60,11 @@ const environment = createEnvironment({
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -4,6 +4,9 @@ import { RecordProxy } from "relay-runtime";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -20,6 +23,7 @@ const environment = createEnvironment({
logNetwork: false,
resolvers,
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(0, "authRevision");
localRecord.setValue("unknown-asset-id", "assetID");
localRecord.setValue("unknown-comment-id", "commentID");
},
@@ -27,7 +31,11 @@ const environment = createEnvironment({
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -5,6 +5,9 @@ import sinon from "sinon";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -47,12 +50,17 @@ const environment = createEnvironment({
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(assetStub.id, "assetID");
localRecord.setValue("unknown-comment-id", "commentID");
localRecord.setValue(0, "authRevision");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -5,6 +5,9 @@ import sinon from "sinon";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -26,12 +29,17 @@ const environment = createEnvironment({
resolvers,
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(assetWithReplies.id, "assetID");
localRecord.setValue(0, "authRevision");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -5,6 +5,9 @@ import sinon from "sinon";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -26,12 +29,17 @@ const environment = createEnvironment({
resolvers,
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(assets[0].id, "assetID");
localRecord.setValue(0, "authRevision");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -5,6 +5,9 @@ import sinon from "sinon";
import { timeout } from "talk-common/utils";
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
import { PostMessageService } from "talk-framework/lib/postMessage";
import { RestClient } from "talk-framework/lib/rest";
import { createInMemoryStorage } from "talk-framework/lib/storage";
import AppContainer from "talk-stream/containers/AppContainer";
import createEnvironment from "./createEnvironment";
@@ -83,12 +86,17 @@ const environment = createEnvironment({
resolvers,
initLocalState: (localRecord: RecordProxy) => {
localRecord.setValue(assetStub.id, "assetID");
localRecord.setValue(0, "authRevision");
},
});
const context: TalkContext = {
relayEnvironment: environment,
localeMessages: [],
localeBundles: [],
localStorage: createInMemoryStorage(),
sessionStorage: createInMemoryStorage(),
rest: new RestClient("http://localhost/api"),
postMessage: new PostMessageService(),
};
const testRenderer = TestRenderer.create(
@@ -1,5 +1,6 @@
.root {
composes: buttonReset from "talk-ui/shared/buttonReset.css";
text-transform: capitalize;
}
.keyboardFocus {
@@ -32,6 +32,8 @@ interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLButtonElement>;
type?: "submit" | "reset" | "button";
}
/**
@@ -45,15 +47,15 @@ const BaseButton: StatelessComponent<InnerProps> = ({
keyboardFocus,
mouseHover,
forwardRef,
type: typeProp,
type,
...rest
}) => {
let Element = "button";
if (anchor) {
Element = "a";
}
let type = typeProp;
if (anchor && type) {
// tslint:disable:next-line: no-console
console.warn(
@@ -69,7 +71,9 @@ const BaseButton: StatelessComponent<InnerProps> = ({
[classes.mouseHover]: mouseHover,
});
return <Element {...rest} className={rootClassName} ref={forwardRef} />;
return (
<Element {...rest} className={rootClassName} ref={forwardRef} type={type} />
);
};
const enhanced = withForwardRef(
@@ -9,6 +9,7 @@ exports[`renders as anchor 1`] = `
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Push Me
</a>
@@ -23,6 +24,7 @@ exports[`renders correctly 1`] = `
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
Push Me
</button>
@@ -282,3 +282,63 @@
}
}
}
.variantUnderlined {
padding-right: 0;
padding-left: 0;
border-radius: 0;
border-bottom: 1px solid currentColor;
padding: 0;
&.colorRegular {
color: var(--palette-grey-main);
}
&.colorPrimary {
color: var(--palette-primary-main);
}
&.colorError {
color: var(--palette-error-main);
}
&.colorSuccess {
color: var(--palette-success-main);
}
&:not(.disabled) {
&.colorRegular {
&.mouseHover {
color: var(--palette-grey-light);
}
&:active,
&.active {
color: var(--palette-grey-lighter);
}
}
&.colorPrimary {
&.mouseHover {
color: var(--palette-primary-light);
}
&:active,
&.active {
color: var(--palette-primary-lighter);
}
}
&.colorError {
&.mouseHover {
color: var(--palette-error-light);
}
&:active,
&.active {
color: var(--palette-error-lighter);
}
}
&.colorSuccess {
&.mouseHover {
color: var(--palette-success-light);
}
&:active,
&.active {
color: var(--palette-success-lighter);
}
}
}
}
@@ -13,7 +13,7 @@ import Flex from '../Flex'
## Regular Button
<Playground>
<Flex itemGutter>
<Flex itemGutter wrap>
<Button>Push Me</Button>
<Button size="small">Push Me</Button>
<Button size="large">Push Me</Button>
@@ -29,7 +29,7 @@ import Flex from '../Flex'
## Filled Button
<Playground>
<Flex itemGutter>
<Flex itemGutter wrap>
<Button variant="filled">Push Me</Button>
<Button variant="filled" size="small">Push Me</Button>
<Button variant="filled" size="large">Push Me</Button>
@@ -45,7 +45,7 @@ import Flex from '../Flex'
## Outlined Button
<Playground>
<Flex itemGutter>
<Flex itemGutter wrap>
<Button variant="outlined">Push Me</Button>
<Button variant="outlined" size="small">Push Me</Button>
<Button variant="outlined" size="large">Push Me</Button>
@@ -61,7 +61,7 @@ import Flex from '../Flex'
## Ghost Button
<Playground>
<Flex itemGutter>
<Flex itemGutter wrap>
<Button variant="ghost">Push Me</Button>
<Button variant="ghost" size="small">Push Me</Button>
<Button variant="ghost" size="large">Push Me</Button>
@@ -28,7 +28,7 @@ interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
color?: "regular" | "primary" | "error" | "success";
/** Variant of the button */
variant?: "regular" | "filled" | "outlined" | "ghost";
variant?: "regular" | "filled" | "outlined" | "ghost" | "underlined";
/** If set renders a full width button */
fullWidth?: boolean;
@@ -36,6 +36,8 @@ interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
/** If set renders active state e.g. to implement toggle buttons */
active?: boolean;
type?: "submit" | "reset" | "button";
/** Internal: Forwarded Ref */
forwardRef?: Ref<HTMLButtonElement>;
}
@@ -57,6 +59,7 @@ export class Button extends React.Component<InnerProps> {
disabled,
forwardRef,
variant,
type,
...rest
} = this.props;
@@ -72,6 +75,7 @@ export class Button extends React.Component<InnerProps> {
[classes.variantFilled]: variant === "filled",
[classes.variantOutlined]: variant === "outlined",
[classes.variantGhost]: variant === "ghost",
[classes.variantUnderlined]: variant === "underlined",
[classes.fullWidth]: fullWidth,
[classes.active]: active,
[classes.disabled]: disabled,
@@ -83,6 +87,7 @@ export class Button extends React.Component<InnerProps> {
classes={pick(classes, "keyboardFocus", "mouseHover")}
disabled={disabled}
forwardRef={forwardRef}
type={type}
{...rest}
/>
);
@@ -5,19 +5,19 @@ menu: UI Kit
import { Playground, PropsTable } from 'docz'
import CallOut from './CallOut'
import Flex from '../Flex'
import HorizontalGutter from '../HorizontalGutter'
# CallOut
## Basic Use
<Playground>
<Flex itemGutter direction="column">
<HorizontalGutter>
<CallOut>This is a component for a callout. Any text that you are wanting to draw attention to such as community guidelines, announcements, etc. can be placed in something like a callout box. The color of the callout box can be customized according your own needs.</CallOut>
<CallOut color="primary">This is a component for a callout. Any text that you are wanting to draw attention to such as community guidelines, announcements, etc. can be placed in something like a callout box. The color of the callout box can be customized according your own needs.</CallOut>
<CallOut color="error">This is a component for a callout. Any text that you are wanting to draw attention to such as community guidelines, announcements, etc. can be placed in something like a callout box. The color of the callout box can be customized according your own needs.</CallOut>
<CallOut color="error">The email address or password you entered is incorrect. Try again</CallOut>
<CallOut color="error" fullWidth>The email address or password you entered is incorrect. Try again</CallOut>
</Flex>
</HorizontalGutter>
</Playground>

Some files were not shown because too many files have changed in this diff Show More