diff --git a/config/jest-client.config.js b/config/jest-client.config.js new file mode 100644 index 000000000..3bfbd195c --- /dev/null +++ b/config/jest-client.config.js @@ -0,0 +1,40 @@ +const path = require("path"); + +module.exports = { + displayName: "client", + rootDir: "../", + roots: ["/src/core/client"], + collectCoverageFrom: ["**/*.{js,jsx,mjs,ts,tsx}"], + coveragePathIgnorePatterns: ["/node_modules/"], + setupFiles: [ + "/config/polyfills.js", + "/src/core/client/test/setup.ts", + ], + testMatch: ["**/*.spec.{js,jsx,mjs,ts,tsx}"], + testEnvironment: "node", + testURL: "http://localhost", + transform: { + "^.+\\.tsx?$": "/node_modules/ts-jest", + "^.+\\.css$": "/config/jest/cssTransform.js", + "^(?!.*\\.(js|jsx|mjs|ts|tsx|css|json|ftl)$)": + "/config/jest/fileTransform.js", + }, + transformIgnorePatterns: [ + "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|ts|tsx)$", + ], + moduleNameMapper: { + "^talk-admin/(.*)$": "/src/core/client/admin/$1", + "^talk-ui/(.*)$": "/src/core/client/ui/$1", + "^talk-stream/(.*)$": "/src/core/client/stream/$1", + "^talk-framework/(.*)$": "/src/core/client/framework/$1", + "^talk-common/(.*)$": "/src/core/common/$1", + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + snapshotSerializers: ["enzyme-to-json/serializer"], + globals: { + "ts-jest": { + useBabelrc: true, + tsConfigFile: path.resolve(__dirname, "tsconfig.jest.json"), + }, + }, +}; diff --git a/config/jest-server.config.js b/config/jest-server.config.js new file mode 100644 index 000000000..6ac9556bf --- /dev/null +++ b/config/jest-server.config.js @@ -0,0 +1,19 @@ +module.exports = { + displayName: "server", + rootDir: "../", + roots: ["/src"], + collectCoverageFrom: ["**/*.{js,jsx,mjs,ts,tsx}"], + coveragePathIgnorePatterns: ["/node_modules/"], + testMatch: ["**/*.spec.{js,jsx,mjs,ts,tsx}"], + testPathIgnorePatterns: ["/node_modules/", "/client/"], + testEnvironment: "node", + testURL: "http://localhost", + transform: { + "^.+\\.tsx?$": "/node_modules/ts-jest", + }, + moduleNameMapper: { + "^talk-server/(.*)$": "/src/core/server/$1", + "^talk-common/(.*)$": "/src/core/common/$1", + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], +}; diff --git a/config/jest.config.js b/config/jest.config.js index ed9b641ae..3e7166a29 100644 --- a/config/jest.config.js +++ b/config/jest.config.js @@ -1,43 +1,6 @@ -const paths = require("./paths"); - module.exports = { - rootDir: "../", - roots: ["/src", "/scripts"], - collectCoverageFrom: ["src/**/*.{js,jsx,mjs,ts,tsx}"], - coveragePathIgnorePatterns: ["/node_modules/"], - setupFiles: ["/config/polyfills.js"], - testMatch: [ - "**/__tests__/**/*.{js,jsx,mjs,ts,tsx}", - "**/*.spec.{js,jsx,mjs,ts,tsx}", - ], - testEnvironment: "node", - testURL: "http://localhost", - transform: { - "^.+\\.(js|jsx|mjs|ts|tsx)$": "/node_modules/ts-jest", - "^.+\\.css$": "/config/jest/cssTransform.js", - "^(?!.*\\.(js|jsx|mjs|css|json|ftl)$)": - "/config/jest/fileTransform.js", - }, - transformIgnorePatterns: [ - "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|ts|tsx)$", - ], - moduleNameMapper: { - "^talk-admin/(.*)$": "/src/core/client/admin/$1", - "^talk-ui/(.*)$": "/src/core/client/ui/$1", - "^talk-stream/(.*)$": "/src/core/client/stream/$1", - "^talk-framework/(.*)$": "/src/core/client/framework/$1", - "^talk-common/(.*)$": "/src/core/common/$1", - "^talk-server/(.*)$": "/src/core/server/$1", - }, - moduleFileExtensions: [ - "web.js", - "js", - "json", - "web.jsx", - "jsx", - "node", - "mjs", - "ts", - "tsx", + projects: [ + "/jest-client.config.js", + "/jest-server.config.js", ], }; diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js index 90b6bfa63..98d4e9a36 100644 --- a/config/jest/cssTransform.js +++ b/config/jest/cssTransform.js @@ -4,7 +4,7 @@ // Copyright https://github.com/Connormiha/jest-css-modules-transform/graphs/contributors // This oututs `module.exports = cssObject` instead of `module.exports = { default: cssObject }`; -const { sep: pathSep, resolve } = require("path"); +const { sep: pathSep, resolve, basename, extname } = require("path"); const postcss = require("postcss"); const postcssNested = postcss([require("postcss-nested")]); @@ -88,7 +88,18 @@ module.exports = { process(src, path, config) { const filename = path.slice(path.lastIndexOf(pathSep) + 1); const textCSS = postcssNested.process(src).css; - const exprt = JSON.stringify(getCSSSelectors(textCSS, path)); + const selectors = getCSSSelectors(textCSS, path); + const component = basename(path, extname(path)); + + // Prefix class names with component name. + Object.keys(selectors).forEach(k => { + selectors[k] = selectors[k] + .split(/\s+/) + .map(v => `${component}-${v}`) + .join(" "); + }); + + const exprt = JSON.stringify(selectors); return `module.exports = ${exprt}`; }, }; diff --git a/config/paths.js b/config/paths.js index 4d852d0f6..7d98ac998 100644 --- a/config/paths.js +++ b/config/paths.js @@ -53,6 +53,7 @@ module.exports = { appTsconfig: resolveApp("src/core/client/tsconfig.json"), appLocales: resolveApp("src/locales"), appThemeVariables: resolveApp("src/core/client/ui/theme/variables.ts"), + appThemeVariablesCSS: resolveApp("src/core/client/ui/theme/variables.css"), testsSetup: resolveApp("src/setupTests.js"), appNodeModules: resolveApp("node_modules"), diff --git a/config/postcss.config.js b/config/postcss.config.js index 4de2da6b2..da91c22b2 100644 --- a/config/postcss.config.js +++ b/config/postcss.config.js @@ -1,27 +1,73 @@ -const precss = require("precss"); -const autoprefixer = require("autoprefixer"); -const fontMagician = require("postcss-font-magician"); +// Allow importing typescript files. +require("ts-node/register"); + const kebabCase = require("lodash/kebabCase"); const mapKeys = require("lodash/mapKeys"); +const mapValues = require("lodash/mapValues"); +const pickBy = require("lodash/pickBy"); const flat = require("flat"); -const flexbugsFixes = require("postcss-flexbugs-fixes"); const paths = require("./paths"); +const autoprefixer = require("autoprefixer"); +const postcssFontMagician = require("postcss-font-magician"); +const postcssFlexbugsFixes = require("postcss-flexbugs-fixes"); +const postcssVariables = require("postcss-css-variables"); +const postcssPresetEnv = require("postcss-preset-env"); +const postcssNested = require("postcss-nested"); +const postcssImport = require("postcss-import"); +const postcssPrependImports = require("postcss-prepend-imports"); +const postcssAdvancedVariables = require("postcss-advanced-variables"); delete require.cache[paths.appThemeVariables]; -const variables = require(paths.appThemeVariables); +const variables = require(paths.appThemeVariables).default; const flatKebabVariables = mapKeys( - flat(variables, { delimiter: "-" }), + mapValues(flat(variables, { delimiter: "-" }), v => v.toString()), (_, k) => kebabCase(k) ); +// These are the default css standard variables. +const cssVariables = pickBy( + flatKebabVariables, + (v, k) => !k.startsWith("breakpoints-") +); + +// These are sass style variables used in media queries. +const mediaQueryVariables = mapValues( + pickBy(flatKebabVariables, (v, k) => k.startsWith("breakpoints-")), + // Add unit to breakpoints. + // Add 1 to support mobile first approach where we start + // with the smallest screen and gradually add styling for the + // next bigger screen. This is realized using `min-width` without + // ever using `max-width`. + v => `${Number.parseInt(v) + 1}px` +); + module.exports = { // Necessary for external CSS imports to work // https://github.com/facebookincubator/create-react-app/issues/2677 ident: "postcss", plugins: [ - precss({ variables: flatKebabVariables }), - fontMagician(), - flexbugsFixes, + // This allows us to define dynamic css variables. + postcssPrependImports({ + path: "", + files: [paths.appThemeVariablesCSS], + }), + // Needed by above plugin. + postcssImport(), + // Support nesting. + postcssNested(), + // Sass style variables to be used in media queries. + postcssAdvancedVariables({ variables: mediaQueryVariables }), + // CSS standard variables for everything else. + postcssVariables({ + variables: cssVariables, + }), + // Provides a modern CSS environment. + postcssPresetEnv(), + // Does all the font handling logic. + postcssFontMagician(), + // Fix known flexbox bugs. + postcssFlexbugsFixes, + // Vendor prefixing. autoprefixer({ browsers: [ ">1%", diff --git a/config/tsconfig.jest.json b/config/tsconfig.jest.json new file mode 100644 index 000000000..d457255e7 --- /dev/null +++ b/config/tsconfig.jest.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "es2015", + "module": "es2015" + } +} diff --git a/config/watcher.ts b/config/watcher.ts index 0d3bf23c1..89da62bf6 100644 --- a/config/watcher.ts +++ b/config/watcher.ts @@ -13,9 +13,14 @@ const config: Config = { "core/client/stream/**/*.ts", "core/client/stream/**/*.tsx", "core/client/stream/**/*.graphql", - "core/client/server/**/*.graphql", + "core/server/**/*.graphql", + ], + ignore: [ + "core/**/*.d.ts", + "core/**/*.graphql.ts", + "**/test/**/*", + "core/**/*.spec.*", ], - ignore: ["core/**/*.d.ts", "core/**/*.graphql.ts"], executor: new CommandExecutor("npm run compile:relay-stream", { runOnInit: true, }), diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 80872c5a4..fcbb75fe2 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -224,6 +224,11 @@ module.exports = { jsx: "preserve", noEmit: false, }, + + // Overwrites the behavior of `include` and `exclude` to only + // include files that are actually being imported and which + // are necessary to compile the bundle. + onlyCompileBundledFiles: true, }, }, ], diff --git a/package-lock.json b/package-lock.json index a0c6f82dc..9d09a9577 100644 --- a/package-lock.json +++ b/package-lock.json @@ -773,14 +773,115 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.0.0-beta.49", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.49.tgz", - "integrity": "sha1-Cfs0XVknwro72J582xOlUGftOaA=", + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.52.tgz", + "integrity": "sha1-AQTvGDzcL9Q9CGAhHMzOee8YAX4=", "dev": true, "requires": { - "@babel/helper-module-transforms": "7.0.0-beta.49", - "@babel/helper-plugin-utils": "7.0.0-beta.49", - "@babel/helper-simple-access": "7.0.0-beta.49" + "@babel/helper-module-transforms": "7.0.0-beta.52", + "@babel/helper-plugin-utils": "7.0.0-beta.52", + "@babel/helper-simple-access": "7.0.0-beta.52" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.52.tgz", + "integrity": "sha1-GSSDv6DR5GfBAVccIQKcy3SvKAE=", + "dev": true, + "requires": { + "@babel/highlight": "7.0.0-beta.52" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.52.tgz", + "integrity": "sha1-cIQOg66JH5RwLGxhN4fEjuPJZbs=", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.52", + "lodash": "^4.17.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-beta.52.tgz", + "integrity": "sha1-vIRE6tJSo3LJKJlq4XM96vOwjJA=", + "dev": true, + "requires": { + "@babel/helper-module-imports": "7.0.0-beta.52", + "@babel/helper-simple-access": "7.0.0-beta.52", + "@babel/helper-split-export-declaration": "7.0.0-beta.52", + "@babel/template": "7.0.0-beta.52", + "@babel/types": "7.0.0-beta.52", + "lodash": "^4.17.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-beta.52.tgz", + "integrity": "sha1-LwWMX3w6X+S8IZA2sueOEb3et60=", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.0.0-beta.52.tgz", + "integrity": "sha1-0plc6cTJ8D/nKvkiNzZ3qOtkJO4=", + "dev": true, + "requires": { + "@babel/template": "7.0.0-beta.52", + "@babel/types": "7.0.0-beta.52", + "lodash": "^4.17.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.52.tgz", + "integrity": "sha1-SqxPMOpjhK82duBLUkZydjLkYN8=", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.52" + } + }, + "@babel/highlight": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.52.tgz", + "integrity": "sha1-7ySTFDLwYVXnvDnNuKaze0oos9A=", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "@babel/parser": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.52.tgz", + "integrity": "sha1-TpNbYs2b+HK9N7zx9j2C/nsCN6I=", + "dev": true + }, + "@babel/template": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.52.tgz", + "integrity": "sha1-ROGPrDglH1f5JRHWdI8JWrAvmW4=", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.52", + "@babel/parser": "7.0.0-beta.52", + "@babel/types": "7.0.0-beta.52", + "lodash": "^4.17.5" + } + }, + "@babel/types": { + "version": "7.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.52.tgz", + "integrity": "sha1-o+ViCxU0slOlCrzyIitSDiOxbaI=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/plugin-transform-modules-systemjs": { @@ -1090,6 +1191,19 @@ "browserslist": "^3.0.0", "invariant": "^2.2.2", "semver": "^5.3.0" + }, + "dependencies": { + "@babel/plugin-transform-modules-commonjs": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.49.tgz", + "integrity": "sha1-Cfs0XVknwro72J582xOlUGftOaA=", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "7.0.0-beta.49", + "@babel/helper-plugin-utils": "7.0.0-beta.49", + "@babel/helper-simple-access": "7.0.0-beta.49" + } + } } }, "@babel/preset-flow": { @@ -1209,16 +1323,6 @@ "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", "dev": true }, - "@csstools/postcss-image-set-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-image-set-function/-/postcss-image-set-function-1.0.0.tgz", - "integrity": "sha512-B+uThZhN3rTRV7mjw3c+GCxIrBkHIAihnfZEb5Ig3+hauQMWGARD3j8UoMX7Hk6w4pBpMbm3Ggs9TVKvtn/kkA==", - "dev": true, - "requires": { - "postcss": "^6.0.20", - "postcss-value-parser": "^3.3.0" - } - }, "@csstools/sass-import-resolve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@csstools/sass-import-resolve/-/sass-import-resolve-1.0.0.tgz", @@ -1403,6 +1507,15 @@ "lodash.deburr": "^4.1.0" } }, + "@sinonjs/formatio": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "dev": true, + "requires": { + "samsam": "1.3.0" + } + }, "@types/bluebird": { "version": "3.5.20", "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.20.tgz", @@ -1438,6 +1551,12 @@ "@types/node": "*" } }, + "@types/cheerio": { + "version": "0.22.8", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.8.tgz", + "integrity": "sha512-LzF540VOFabhS2TR2yYFz2Mu/fTfkA+5AwYddtJbOJGwnYrr2e7fHadT7/Z3jNGJJdCRlO3ySxmW26NgRdwhNA==", + "dev": true + }, "@types/chokidar": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz", @@ -1496,6 +1615,25 @@ "@types/node": "*" } }, + "@types/enzyme": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.1.11.tgz", + "integrity": "sha512-abPTpLuveNVd2ibafCjwoZT9MerzgnBKd6ijNKdtlfJREGKXk5dxzKAXGoY9IuiYarH2YXTc197WeyIVQUFjQg==", + "dev": true, + "requires": { + "@types/cheerio": "*", + "@types/react": "*" + } + }, + "@types/enzyme-adapter-react-16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.2.tgz", + "integrity": "sha512-/oEtlwJyFIT9metXC2A90XnjfHwBDYxhFoJwqNjNDG5K2CCqp7xneQbAp4u5j280bOmalFYUDjfmmxNQG3S4Og==", + "dev": true, + "requires": { + "@types/enzyme": "*" + } + }, "@types/events": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", @@ -1552,6 +1690,18 @@ "integrity": "sha512-nOnsbHvoo5DsQEh8VGlbQlfg9+/iFSxE5RQKLNkAODIqyupdEkBCZf6RCNxR+9X0egMIkJ43NnwkEJKxLogsIA==", "dev": true }, + "@types/jsdom": { + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-11.0.6.tgz", + "integrity": "sha512-6sw9iNdXak3Dk4iN1osdZeZcV8cTe2SNIVwbnt8MjFFAI8+nBWDNcDfjUgZZqycF3Twlr7xXlKntnOrruxNRvg==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^4.0.0" + } + }, "@types/lodash": { "version": "4.14.109", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.109.tgz", @@ -1639,6 +1789,15 @@ "from": "github:coralproject/patched#types/react-relay", "dev": true }, + "@types/react-responsive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/react-responsive/-/react-responsive-3.0.1.tgz", + "integrity": "sha512-ti7MKe0AuWCGpNkRzRsRB8ExS7NZotXtDtD27rX4HiZML37SetbIp3cw5FDXIeqIC7a3TzWuPjv/I9Qcu4YL3w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-test-renderer": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.0.1.tgz", @@ -1680,6 +1839,18 @@ "@types/mime": "*" } }, + "@types/sinon": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-5.0.1.tgz", + "integrity": "sha512-yxzBCIjE3lp9lYjfBbIK/LRCoXgCLLbIIBIje7eNCcUIIR2CZZtyX5uto2hVoMSMqLrsRrT6mwwUEd0yFgOwpA==", + "dev": true + }, + "@types/tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==", + "dev": true + }, "@types/uuid": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.3.tgz", @@ -2550,45 +2721,10 @@ } }, "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, - "dependencies": { - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - } - } + "version": "7.0.0-bridge.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "dev": true }, "babel-generator": { "version": "6.26.1", @@ -4173,6 +4309,45 @@ "source-map-support": "^0.4.15" }, "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -4355,9 +4530,9 @@ "dev": true }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "optional": true, "requires": { @@ -4995,6 +5170,54 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "cheerio": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", + "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", + "dev": true, + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + }, + "dependencies": { + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "dev": true, + "requires": { + "@types/node": "*" + } + } + } + }, "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", @@ -6018,6 +6241,12 @@ } } }, + "css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA=", + "dev": true + }, "css-modules-loader-core": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", @@ -6154,9 +6383,9 @@ "dev": true }, "cssdb": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-1.6.0.tgz", - "integrity": "sha512-KBxrzWDyY0aFA3DkAH0SDWhIKp1or83pBLqacXq4VWNrOCwf/G9An+VDXIW8qAGJz11o9nO8mQezq1ZJOdaF8A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-3.1.0.tgz", + "integrity": "sha512-9+ASPG9O+d876YfZJjStXszOGn3EEOp5scMeCwZb/+TwTZfadQFshz/Vsh1vGET9HzaXvsfV+1O0ShFb52DddA==", "dev": true }, "cssesc": { @@ -6290,9 +6519,9 @@ } }, "cssom": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", - "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", + "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", "dev": true }, "cssstyle": { @@ -6673,6 +6902,12 @@ "is-woff2": "^1.0.0" } }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", + "dev": true + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -7326,6 +7561,65 @@ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "dev": true }, + "enzyme": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.3.0.tgz", + "integrity": "sha512-l8csyPyLmtxskTz6pX9W8eDOyH1ckEtDttXk/vlFWCjv00SkjTjtoUrogqp4yEvMyneU9dUJoOLnqFoiHb8IHA==", + "dev": true, + "requires": { + "cheerio": "^1.0.0-rc.2", + "function.prototype.name": "^1.0.3", + "has": "^1.0.1", + "is-boolean-object": "^1.0.0", + "is-callable": "^1.1.3", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-subset": "^0.1.1", + "lodash": "^4.17.4", + "object-inspect": "^1.5.0", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4", + "object.values": "^1.0.4", + "raf": "^3.4.0", + "rst-selector-parser": "^2.2.3" + } + }, + "enzyme-adapter-react-16": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz", + "integrity": "sha512-kC8pAtU2Jk3OJ0EG8Y2813dg9Ol0TXi7UNxHzHiWs30Jo/hj7alc//G1YpKUsPP1oKl9X+Lkx+WlGJpPYA+nvw==", + "dev": true, + "requires": { + "enzyme-adapter-utils": "^1.3.0", + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "object.values": "^1.0.4", + "prop-types": "^15.6.0", + "react-reconciler": "^0.7.0", + "react-test-renderer": "^16.0.0-0" + } + }, + "enzyme-adapter-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.3.0.tgz", + "integrity": "sha512-vVXSt6uDv230DIv+ebCG66T1Pm36Kv+m74L1TrF4kaE7e1V7Q/LcxO0QRkajk5cA6R3uu9wJf5h13wOTezTbjA==", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "prop-types": "^15.6.0" + } + }, + "enzyme-to-json": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz", + "integrity": "sha1-Z8YEDpMRgvGDQYry659DIyWKp38=", + "dev": true, + "requires": { + "lodash": "^4.17.4" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -8881,6 +9175,17 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz", + "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "is-callable": "^1.1.3" + } + }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -9851,6 +10156,12 @@ "decamelize": "^1.0.0" } }, + "hyphenate-style-name": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", + "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=", + "dev": true + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -10291,6 +10602,12 @@ "binary-extensions": "^1.0.0" } }, + "is-boolean-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz", + "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=", + "dev": true + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -10495,6 +10812,12 @@ } } }, + "is-number-object": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz", + "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=", + "dev": true + }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -10616,6 +10939,18 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-string": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz", + "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=", + "dev": true + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, "is-svg": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", @@ -11024,6 +11359,39 @@ "pretty-format": "^23.2.0" }, "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -11037,6 +11405,12 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true } } }, @@ -11265,6 +11639,47 @@ "strip-bom": "3.0.0", "write-file-atomic": "^2.1.0", "yargs": "^11.0.0" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + } } }, "jest-serializer": { @@ -11501,6 +11916,12 @@ "verror": "1.10.0" } }, + "just-extend": { + "version": "1.1.27", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", + "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", + "dev": true + }, "keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", @@ -11954,6 +12375,12 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, "lodash.foreach": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", @@ -12104,6 +12531,12 @@ "object.assign": "^4.1.0" } }, + "lolex": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz", + "integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==", + "dev": true + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -12236,6 +12669,15 @@ "integrity": "sha1-iQwsGzv+g/sA5BKbjkz+ZFJw+dE=", "dev": true }, + "matchmediaquery": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.2.1.tgz", + "integrity": "sha1-IjxwBXk94D5HzpKxMoWnLEStos8=", + "dev": true, + "requires": { + "css-mediaquery": "^0.1.2" + } + }, "math-expression-evaluator": { "version": "1.2.17", "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", @@ -12753,6 +13195,18 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, + "nearley": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.13.0.tgz", + "integrity": "sha512-ioYYogSaZhFlCpRizQgY3UT3G1qFXmHGY/5ozoFE3dMfiCRAeJfh+IPE3/eh9gCZvqLhPCWb4bLt7Bqzo+1mLQ==", + "dev": true, + "requires": { + "nomnom": "~1.6.2", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6", + "semver": "^5.4.1" + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -12776,6 +13230,36 @@ "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", "dev": true }, + "nise": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz", + "integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^2.0.0", + "just-extend": "^1.1.27", + "lolex": "^2.3.2", + "path-to-regexp": "^1.7.0", + "text-encoding": "^0.6.4" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", @@ -12868,6 +13352,24 @@ "which": "^1.3.0" } }, + "nomnom": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", + "dev": true, + "requires": { + "colors": "0.5.x", + "underscore": "~1.4.4" + }, + "dependencies": { + "colors": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=", + "dev": true + } + } + }, "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -13047,6 +13549,18 @@ } } }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "dev": true + }, "object-keys": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", @@ -13074,6 +13588,18 @@ "object-keys": "^1.0.11" } }, + "object.entries": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", + "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.6.1", + "function-bind": "^1.1.0", + "has": "^1.0.1" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -13103,6 +13629,18 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", + "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.6.1", + "function-bind": "^1.1.0", + "has": "^1.0.1" + } + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -13617,92 +14155,33 @@ "postcss": "^6" } }, - "postcss-apply": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/postcss-apply/-/postcss-apply-0.9.0.tgz", - "integrity": "sha512-Ysel7CyF7FiZQno9oADqKXsfZw4DvTcQXtFvN1nLZQA3woRiVYV2M5kGJSrqQVWGjp/zqNUjUpXHs24TgxFjxg==", - "dev": true, - "requires": { - "babel-runtime": "^6.23.0", - "balanced-match": "^0.4.2", - "postcss": "^6.0.0" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "postcss-atroot": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/postcss-atroot/-/postcss-atroot-0.1.3.tgz", - "integrity": "sha1-Z1LAIwx0UUBUk0WysOMOvtoBpAU=", - "dev": true, - "requires": { - "postcss": "^5.0.5" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, "postcss-attribute-case-insensitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-2.0.0.tgz", - "integrity": "sha1-lNxCLI+QmX8WvTOjZUu77AhJY7Q=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-3.0.1.tgz", + "integrity": "sha512-X/viSS9YrAoDnRa2R4sElsAmW+scOWeVW11FjWQN8m+FW1YY0jdIA9fuBSSF1pKsJTYXJXGJ1oAjFHl8cqcmKw==", "dev": true, "requires": { - "postcss": "^6.0.0", - "postcss-selector-parser": "^2.2.3" + "postcss": "^6.0.23", + "postcss-selector-parser": "^4.0.0" + }, + "dependencies": { + "cssesc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-1.0.1.tgz", + "integrity": "sha512-S2hzrpWvE6G/rW7i7IxJfWBYn27QWfOIncUW++8Rbo1VB5zsJDSVPcnI+Q8z7rhxT6/yZeLOCja4cZnghJrNGA==", + "dev": true + }, + "postcss-selector-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-4.0.0.tgz", + "integrity": "sha512-5h+MvEjnzu1qy6MabjuoPatsGAjjDV9B24e7Cktjl+ClNtjVjmvAXjOFQr1u7RlWULKNGYaYVE4s+DIIQ4bOGA==", + "dev": true, + "requires": { + "cssesc": "^1.0.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } } }, "postcss-calc": { @@ -13766,6 +14245,16 @@ } } }, + "postcss-color-functional-notation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-1.0.1.tgz", + "integrity": "sha512-8ECwUd75SwcjLMzFdRVRBqjVoHwGeWpLQKCNIQo9T+QeCrUNKI1NJ6rgpni5vo7iZ7MZy21qKqUieMM3D0wHYQ==", + "dev": true, + "requires": { + "postcss": "^6.0.22", + "postcss-values-parser": "^1.5.0" + } + }, "postcss-color-hex-alpha": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-3.0.0.tgz", @@ -13820,16 +14309,6 @@ "postcss-values-parser": "^1.5.0" } }, - "postcss-color-rgb": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-rgb/-/postcss-color-rgb-2.0.0.tgz", - "integrity": "sha1-FFOcinExSUtILg3RzCZf9lFLUmM=", - "dev": true, - "requires": { - "postcss": "^6.0.1", - "postcss-value-parser": "^3.3.0" - } - }, "postcss-colormin": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", @@ -13951,6 +14430,17 @@ } } }, + "postcss-css-variables": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/postcss-css-variables/-/postcss-css-variables-0.9.0.tgz", + "integrity": "sha512-pSNj57bdBaFnyDzt96SZOidMMWY7Q5OySQdKsTxaxehYO6EtNC5SAKIwjah/poh5vUmNthkrlOd03bP+HNGuCA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.3", + "extend": "^3.0.1", + "postcss": "^6.0.8" + } + }, "postcss-custom-media": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-6.0.0.tgz", @@ -13981,22 +14471,28 @@ } }, "postcss-dir-pseudo-class": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-3.0.0.tgz", - "integrity": "sha512-iEVgue59Xs6vz9CQZtlyonW/BmVfpqWglcUyIP2rQaBpH1a2T8Iax61eXY2NjTAq5r3Xjxwk4lrA84acoAiWHw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-4.0.0.tgz", + "integrity": "sha512-ZAeXMIyZukHHeDt5IFchWB+okPzasb8YnpkXIgTiJl4216X1IplMrODjihZIBDXNE2RdJRBCAOx8uGzCnGSxTA==", "dev": true, "requires": { - "postcss": "^6.0.20", - "postcss-selector-parser": "^3.1.1" + "postcss": "^6.0.22", + "postcss-selector-parser": "^4.0.0" }, "dependencies": { + "cssesc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-1.0.1.tgz", + "integrity": "sha512-S2hzrpWvE6G/rW7i7IxJfWBYn27QWfOIncUW++8Rbo1VB5zsJDSVPcnI+Q8z7rhxT6/yZeLOCja4cZnghJrNGA==", + "dev": true + }, "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-4.0.0.tgz", + "integrity": "sha512-5h+MvEjnzu1qy6MabjuoPatsGAjjDV9B24e7Cktjl+ClNtjVjmvAXjOFQr1u7RlWULKNGYaYVE4s+DIIQ4bOGA==", "dev": true, "requires": { - "dot-prop": "^4.1.1", + "cssesc": "^1.0.1", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } @@ -14299,14 +14795,14 @@ } } }, - "postcss-extend-rule": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-extend-rule/-/postcss-extend-rule-1.1.0.tgz", - "integrity": "sha512-+NXtLOY49Xcx9173SJAYj41esTRTpihSLoKpj5yzoBki9PZK4HF37AH9AVyCeLFJL9fzhh0YFuk/3eyeZ9d/jw==", + "postcss-env-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-1.0.0.tgz", + "integrity": "sha512-UVkdbVCRAEr79XkS6uxMRWIHYrFNuhXmjw6gxyesCBXzzHIvYOoz5UKTWM39xX3j9vGO5waVzxq/VzEiZgsM0g==", "dev": true, "requires": { - "postcss": "^6.0.11", - "postcss-nesting": "^4.2.1" + "postcss": "^6.0.22", + "postcss-values-parser": "^1.5.0" } }, "postcss-filter-plugins": { @@ -14378,14 +14874,23 @@ } }, "postcss-focus-visible": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-2.0.0.tgz", - "integrity": "sha512-nJaq5CK2YBWB1fu1SeK0qXAk0TJncY3Ms7iwFov+J3sNetecvTeCQuSxQCf9U9T9Vjusnb3zzThBs5XwP/eb/g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-3.0.0.tgz", + "integrity": "sha512-6i3HsOrWNelxBYPh/HWAXF9lPwCFAfFVlUTZqsLRXYMPhcXH1eXdItozRBvT9l5pYF4ddJJbgk4JOp0au0QToA==", "dev": true, "requires": { "postcss": "^6.0" } }, + "postcss-focus-within": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-2.0.0.tgz", + "integrity": "sha512-LTbT/dxZ3FahpOv1XZMyRLNnBk5QWVU4HL/p82iFkzoPNVhNQazaYIujHXTOAKea5clgjbj6GdFj7mU7qzy1kQ==", + "dev": true, + "requires": { + "postcss": "^6.0.21" + } + }, "postcss-font-family-system-ui": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-font-family-system-ui/-/postcss-font-family-system-ui-3.0.0.tgz", @@ -14416,6 +14921,37 @@ "postcss": "^6.0.1" } }, + "postcss-gap-properties": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-1.0.0.tgz", + "integrity": "sha512-snL2k0Nie72J0uCsKgfO2Sd5rs3Wlhsk+k9uVzyMaeBH9gouNPPY7tZ4bopRJmBISbZEUtvF8Gchat6nOFQHdg==", + "dev": true, + "requires": { + "postcss": "^6.0.22" + } + }, + "postcss-image-set-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-2.0.0.tgz", + "integrity": "sha512-2bpCIj2wFhIhpyM1G/ZZgBJM8K/VKbEcQ0SX5MI9MTGv4giPMnuXMa/sX3bHXHhi1PL4v2WRfqD1+w5T9EhFqg==", + "dev": true, + "requires": { + "postcss": "^6.0.22", + "postcss-values-parser": "^1.5.0" + } + }, + "postcss-import": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", + "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", + "dev": true, + "requires": { + "postcss": "^6.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, "postcss-initial": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-2.0.0.tgz", @@ -14426,6 +14962,17 @@ "postcss": "^6.0.1" } }, + "postcss-lab-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-1.0.1.tgz", + "integrity": "sha512-kxaHXTRjlBLDpJUMsrGM0GnVgBrMqQz1HfaXdv3VNpz2EwwUhrU9uVfqv/NAtiWt6ZkD7IK2MUlncRoj024lIA==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^6.0.22", + "postcss-values-parser": "^1.5.0" + } + }, "postcss-load-config": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", @@ -15090,15 +15637,6 @@ } } }, - "postcss-nesting": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-4.2.1.tgz", - "integrity": "sha512-IkyWXICwagCnlaviRexi7qOdwPw3+xVVjgFfGsxmztvRVaNxAlrypOIKqDE5mxY+BVxnId1rnUKBRQoNE2VDaA==", - "dev": true, - "requires": { - "postcss": "^6.0.11" - } - }, "postcss-normalize-charset": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", @@ -15280,6 +15818,15 @@ } } }, + "postcss-overflow-shorthand": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-1.0.1.tgz", + "integrity": "sha512-QeJk23W8dLP2DrWYSKTwfFfh4Tcy5Msr58vuuxCPcCijX/07jva0OGNKtUH9vZ6NnXB2WEsnfIIg5M0ScPEWeQ==", + "dev": true, + "requires": { + "postcss": "^6.0.22" + } + }, "postcss-page-break": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-1.0.0.tgz", @@ -15289,60 +15836,154 @@ "postcss": "^6.0.16" } }, - "postcss-preset-env": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-3.5.0.tgz", - "integrity": "sha512-1I7ve+XzmNLJMZDORLOjSpY28t2H1qADEUcp2tQzuLBxAsbWMBUTDpSPsCKBduoqd4zWuH4bI/04W4T4hveHQw==", + "postcss-place": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-3.0.1.tgz", + "integrity": "sha512-6Cg0z39zBU4FOS85Z6+Us+GCIW7UqKdOGH/9j26LwMzZ3L909wG7NP3BF+L12AEeIL5XfI8Q5SWu9Or3nJTS8g==", "dev": true, "requires": { - "@csstools/postcss-image-set-function": "^1.0.0", - "browserslist": "^3.2.4", - "caniuse-lite": "^1.0.30000823", - "cssdb": "^1.6.0", - "postcss": "^6.0.21", - "postcss-apply": "^0.9.0", - "postcss-attribute-case-insensitive": "^2.0.0", + "postcss": "^6.0.22", + "postcss-values-parser": "^1.5.0" + } + }, + "postcss-prepend-imports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-prepend-imports/-/postcss-prepend-imports-1.0.1.tgz", + "integrity": "sha1-v0WBiAhcM/82mVzJbJFojWSYjig=", + "dev": true, + "requires": { + "postcss": "^5.0.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "postcss-preset-env": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-5.2.1.tgz", + "integrity": "sha512-zsGFBKtTyCY5YxOq6Q/WR4oa9/LOKawEWMxJ7UpMtfj1t50OWeGZadRQp/i7DSSWjKus+eJKLIHDxLaobwhidQ==", + "dev": true, + "requires": { + "autoprefixer": "^8.6.3", + "browserslist": "^3.2.8", + "caniuse-lite": "^1.0.30000859", + "cssdb": "^3.1.0", + "postcss": "^6.0.23", + "postcss-attribute-case-insensitive": "^3.0.1", + "postcss-color-functional-notation": "^1.0.1", "postcss-color-hex-alpha": "^3.0.0", "postcss-color-mod-function": "^2.4.2", - "postcss-color-rebeccapurple": "^3.0.0", - "postcss-color-rgb": "^2.0.0", + "postcss-color-rebeccapurple": "^3.1.0", "postcss-custom-media": "^6.0.0", "postcss-custom-properties": "^7.0.0", "postcss-custom-selectors": "^4.0.1", - "postcss-dir-pseudo-class": "^3.0.0", - "postcss-focus-visible": "^2.0.0", + "postcss-dir-pseudo-class": "^4.0.0", + "postcss-env-function": "^1.0.0", + "postcss-focus-visible": "^3.0.0", + "postcss-focus-within": "^2.0.0", "postcss-font-family-system-ui": "^3.0.0", "postcss-font-variant": "^3.0.0", + "postcss-gap-properties": "^1.0.0", + "postcss-image-set-function": "^2.0.0", "postcss-initial": "^2.0.0", + "postcss-lab-function": "^1.0.1", "postcss-logical": "^1.1.1", "postcss-media-minmax": "^3.0.0", - "postcss-nesting": "^4.2.1", + "postcss-nesting": "^6.0.0", + "postcss-overflow-shorthand": "^1.0.1", "postcss-page-break": "^1.0.0", - "postcss-pseudo-class-any-link": "^4.0.0", + "postcss-place": "^3.0.1", + "postcss-pseudo-class-any-link": "^5.0.0", "postcss-replace-overflow-wrap": "^2.0.0", "postcss-selector-matches": "^3.0.1", "postcss-selector-not": "^3.0.1" - } - }, - "postcss-property-lookup": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-property-lookup/-/postcss-property-lookup-2.0.0.tgz", - "integrity": "sha512-KUb53a7UZWDMVb0SRODOonc4H1wlbgQ0VfYwmJaR1xWPorhariEz0U7x0ri3W/imFs6HqLYWP7hl2yMvi5Ty+w==", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "postcss": "^6.0.6", - "tcomb": "^3.2.21" + }, + "dependencies": { + "postcss-nesting": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-6.0.0.tgz", + "integrity": "sha512-Yoglsy6eZbDCbRIXoYSmnIt9ao4xyg07iFwVBd7WyIkDzMSeRxIqUk8xEAdkeJQ7eGfWo6RufrTU7FSUjZ22fg==", + "dev": true, + "requires": { + "postcss": "^6.0.22" + } + } } }, "postcss-pseudo-class-any-link": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-4.0.0.tgz", - "integrity": "sha1-kVKgYT00UHIFE+iJKFS65C0O5o4=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-5.0.0.tgz", + "integrity": "sha512-rA5grdRhLLMMI654eOZVuKGr4fUBQNGSH0K+7j5839CiBA/IvNt/Ewt7aIrkGZPySKI3nqzs34HefO8U0Fxahg==", "dev": true, "requires": { - "postcss": "^6.0.1", - "postcss-selector-parser": "^2.2.3" + "postcss": "^6.0.22", + "postcss-selector-parser": "^4.0.0" + }, + "dependencies": { + "cssesc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-1.0.1.tgz", + "integrity": "sha512-S2hzrpWvE6G/rW7i7IxJfWBYn27QWfOIncUW++8Rbo1VB5zsJDSVPcnI+Q8z7rhxT6/yZeLOCja4cZnghJrNGA==", + "dev": true + }, + "postcss-selector-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-4.0.0.tgz", + "integrity": "sha512-5h+MvEjnzu1qy6MabjuoPatsGAjjDV9B24e7Cktjl+ClNtjVjmvAXjOFQr1u7RlWULKNGYaYVE4s+DIIQ4bOGA==", + "dev": true, + "requires": { + "cssesc": "^1.0.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } } }, "postcss-reduce-idents": { @@ -15782,21 +16423,6 @@ } } }, - "precss": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/precss/-/precss-3.1.2.tgz", - "integrity": "sha512-TAEKJjzNyKIwyDeJ3yuaTl1x0CoFw+VyycR3dO18Kl2rGU/rPNocCYH1cry1sstWwGKl3RpJhDfZ2ZdzscM0IQ==", - "dev": true, - "requires": { - "postcss": "^6.0.19", - "postcss-advanced-variables": "^2.3.3", - "postcss-atroot": "^0.1.3", - "postcss-extend-rule": "^1.1.0", - "postcss-nested": "^3.0.0", - "postcss-preset-env": "^3.2.2", - "postcss-property-lookup": "^2.0.0" - } - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -16064,6 +16690,31 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, + "raf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz", + "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", + "dev": true, + "requires": { + "performance-now": "^2.1.0" + } + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", + "dev": true + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, "randomatic": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", @@ -16577,6 +17228,18 @@ "integrity": "sha512-2bBoBaulpNnmXvM4nTQL3KKdsyzcufHI9ebpzoWlFZAplM0gn7Z0cBOcw2H8FFHGI38OMzo8CSpKYJaOJ8e/KA==", "dev": true }, + "react-reconciler": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.7.0.tgz", + "integrity": "sha512-50JwZ3yNyMS8fchN+jjWEJOH3Oze7UmhxeoJLn2j6f3NjpfCRbcmih83XTWmzqtar/ivd5f7tvQhvvhism2fgg==", + "dev": true, + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" + } + }, "react-relay": { "version": "github:coralproject/patched#aad037f8cbcd9c14d5f6fd96cfafb24c39eab937", "from": "github:coralproject/patched#react-relay", @@ -16587,6 +17250,17 @@ "prop-types": "^15.5.8" } }, + "react-responsive": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-4.1.0.tgz", + "integrity": "sha512-ZuDraf0qsJlyiTwzeva+foHx83IP6SIhru9o7BvMwQ4ZHjRIL5WjdgVNNrKSRbmeWO9rEJoMpabei/5lJn8KaA==", + "dev": true, + "requires": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.2.1", + "prop-types": "^15.6.1" + } + }, "react-router": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", @@ -16671,6 +17345,7 @@ "react-is": "^16.4.1" } }, +<<<<<<< HEAD "react-tooltip": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-3.6.1.tgz", @@ -16678,6 +17353,23 @@ "requires": { "classnames": "^2.2.5", "prop-types": "^15.6.0" +======= + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } +>>>>>>> cac0afa61e79b19ddcae873f543a6910b83c92e7 } }, "read-pkg": { @@ -17195,6 +17887,12 @@ "invariant": "^2.2.2" } }, + "relay-local-schema": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/relay-local-schema/-/relay-local-schema-0.7.0.tgz", + "integrity": "sha1-onxxcMYUi5Fu0PhNbDAAm6MyoOQ=", + "dev": true + }, "relay-runtime": { "version": "github:coralproject/patched#9dda7718393cb3aa6647bd3badf574c025f79d12", "from": "github:coralproject/patched#relay-runtime", @@ -17629,6 +18327,16 @@ "inherits": "^2.0.1" } }, + "rst-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", + "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", + "dev": true, + "requires": { + "lodash.flattendeep": "^4.4.0", + "nearley": "^2.7.10" + } + }, "rsvp": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", @@ -17711,6 +18419,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "dev": true + }, "sane": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", @@ -17972,6 +18686,32 @@ } } }, + "sinon": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.2.tgz", + "integrity": "sha512-MPa7kfV44XgzlXthSI28QgJUAjNQ2LdafqYFQnwXv1d5bCJXrVyYH7PLFk8PpAFhRN5Tgy8PbJFJbrvDsTss8g==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^2.0.0", + "diff": "^3.5.0", + "lodash.get": "^4.4.2", + "lolex": "^2.4.2", + "nise": "^1.3.3", + "supports-color": "^5.4.0", + "type-detect": "^4.0.8" + }, + "dependencies": { + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "sisteransi": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz", @@ -18649,12 +19389,6 @@ "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", "dev": true }, - "tcomb": { - "version": "3.2.27", - "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.27.tgz", - "integrity": "sha512-XWdJW7F/M3YzXhDEUP8ycmNWoYymBtsHwCHoda0YF44RthJsls95TqDrmpAlC1sB/KXaCvkdBlcNRq+AaV6klA==", - "dev": true - }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -18767,6 +19501,12 @@ } } }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -19029,357 +19769,110 @@ "dev": true }, "ts-jest": { - "version": "22.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-22.4.6.tgz", - "integrity": "sha512-kYQ6g1G1AU+bOO9rv+SSQXg4WTcni6Wx3AM48iHni0nP1vIuhdNRjKTE9Cxx36Ix/IOV7L85iKu07dgXJzH2pQ==", + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-23.0.0.tgz", + "integrity": "sha512-drIjGaoIfXgNi0UUxu963M57kgf+NE2xw386UmD/Ai2hmVpKPCcF2xRwnNMobnVfLQw4v6HSblTKuBg6v+pEBQ==", "dev": true, "requires": { - "babel-core": "^6.26.3", "babel-plugin-istanbul": "^4.1.6", - "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", - "babel-preset-jest": "^22.4.3", + "babel-preset-jest": "^23.0.0", "cpx": "^1.5.0", - "fs-extra": "6.0.0", - "jest-config": "^22.4.3", + "fs-extra": "6.0.1", + "jest-config": "^23.0.0", "lodash": "^4.17.10", - "pkg-dir": "^2.0.0", - "source-map-support": "^0.5.5", - "yargs": "^11.0.0" + "pkg-dir": "^3.0.0", + "yargs": "^12.0.1" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { + "decamelize": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "xregexp": "4.0.0" } }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", "dev": true }, - "babel-plugin-jest-hoist": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz", - "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==", - "dev": true - }, - "babel-preset-jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz", - "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==", + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^22.4.4", - "babel-plugin-syntax-object-rest-spread": "^6.13.0" + "find-up": "^3.0.0" } }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "yargs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" } }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "camelcase": "^4.1.0" } - }, - "expect": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz", - "integrity": "sha512-XcNXEPehqn8b/jm8FYotdX0YrXn36qp4HWlrVT4ktwQas1l1LPxiVWncYnnL2eyMtKAmVIaG0XAp0QlrqJaxaA==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "jest-diff": "^22.4.3", - "jest-get-type": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "jest-message-util": "^22.4.3", - "jest-regex-util": "^22.4.3" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "fs-extra": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.0.tgz", - "integrity": "sha512-lk2cUCo8QzbiEWEbt7Cw3m27WMiRG321xsssbcIpfMhpRjrlC08WBOVQqj1/nQYYNnPtyIhP1oqLO3QwT2tPCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "jest-config": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-22.4.4.tgz", - "integrity": "sha512-9CKfo1GC4zrXSoMLcNeDvQBfgtqGTB1uP8iDIZ97oB26RCUb886KkKWhVcpyxVDOUxbhN+uzcBCeFe7w+Iem4A==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^22.4.1", - "jest-environment-node": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-jasmine2": "^22.4.4", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "pretty-format": "^22.4.0" - } - }, - "jest-diff": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz", - "integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff": "^3.2.0", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - } - }, - "jest-environment-jsdom": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz", - "integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==", - "dev": true, - "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz", - "integrity": "sha512-reZl8XF6t/lMEuPWwo9OLfttyC26A5AMgDyEQ6DBgZuyfyeNUzYT8BFo6uxCCP/Av/b7eb9fTi3sIHFPBzmlRA==", - "dev": true, - "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3" - } - }, - "jest-jasmine2": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz", - "integrity": "sha512-nK3vdUl50MuH7vj/8at7EQVjPGWCi3d5+6aCi7Gxy/XMWdOdbH1qtO/LjKbqD8+8dUAEH+BVVh7HkjpCWC1CSw==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^22.4.0", - "graceful-fs": "^4.1.11", - "is-generator-fn": "^1.0.0", - "jest-diff": "^22.4.0", - "jest-matcher-utils": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "source-map-support": "^0.5.0" - } - }, - "jest-matcher-utils": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", - "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - } - }, - "jest-message-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz", - "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0-beta.35", - "chalk": "^2.0.1", - "micromatch": "^2.3.11", - "slash": "^1.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz", - "integrity": "sha512-+4R6mH5M1G4NK16CKg9N1DtCaFmuxhcIqF4lQK/Q1CIotqMs/XBemfpDPeVZBFow6iyUNu6EBT9ugdNOTT5o5Q==", - "dev": true - }, - "jest-regex-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz", - "integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==", - "dev": true - }, - "jest-resolve": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz", - "integrity": "sha512-u3BkD/MQBmwrOJDzDIaxpyqTxYH+XqAXzVJP51gt29H8jpj3QgKof5GGO2uPGKGeA1yTMlpbMs1gIQ6U4vcRhw==", - "dev": true, - "requires": { - "browser-resolve": "^1.11.2", - "chalk": "^2.0.1" - } - }, - "jest-snapshot": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz", - "integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^22.4.3" - } - }, - "jest-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz", - "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==", - "dev": true, - "requires": { - "callsites": "^2.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "jest-message-util": "^22.4.3", - "mkdirp": "^0.5.1", - "source-map": "^0.6.0" - } - }, - "jest-validate": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-22.4.4.tgz", - "integrity": "sha512-dmlf4CIZRGvkaVg3fa0uetepcua44DHtktHm6rcoNVtYlpwe6fEJRkMFsaUVcFHLzbuBJ2cPw9Gl9TKfnzMVwg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-config": "^22.4.4", - "jest-get-type": "^22.1.0", - "leven": "^2.1.0", - "pretty-format": "^22.4.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "pretty-format": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -19554,6 +20047,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -19843,6 +20342,12 @@ "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", "dev": true }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", + "dev": true + }, "unherit": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", @@ -21155,6 +21660,12 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index 8dc74e5d8..7ae7aa084 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "devDependencies": { "@babel/core": "7.0.0-beta.49", "@babel/plugin-syntax-dynamic-import": "7.0.0-beta.49", + "@babel/plugin-transform-modules-commonjs": "^7.0.0-beta.52", "@babel/polyfill": "7.0.0-beta.49", "@babel/preset-env": "7.0.0-beta.49", "@babel/preset-react": "7.0.0-beta.49", @@ -67,11 +68,14 @@ "@types/convict": "^4.2.0", "@types/cross-spawn": "^6.0.0", "@types/dotenv": "^4.0.3", + "@types/enzyme": "^3.1.11", + "@types/enzyme-adapter-react-16": "^1.0.2", "@types/express": "^4.16.0", "@types/graphql": "^0.13.1", "@types/ioredis": "^3.2.8", "@types/jest": "^23.1.1", "@types/joi": "^13.0.8", + "@types/jsdom": "^11.0.6", "@types/lodash": "^4.14.109", "@types/luxon": "^0.5.3", "@types/mongodb": "^3.0.19", @@ -80,12 +84,15 @@ "@types/query-string": "^6.1.0", "@types/react-dom": "^16.0.6", "@types/react-relay": "github:coralproject/patched#types/react-relay", + "@types/react-responsive": "^3.0.1", "@types/react-test-renderer": "^16.0.1", "@types/recompose": "^0.26.1", "@types/relay-runtime": "github:coralproject/patched#types/relay-runtime", + "@types/sinon": "^5.0.1", "@types/uuid": "^3.4.3", "@types/ws": "^5.1.2", "autoprefixer": "^8.6.0", + "babel-core": "^7.0.0-bridge.0", "babel-loader": "^8.0.0-beta", "babel-plugin-relay": "github:coralproject/patched#babel-plugin-relay", "babel-preset-react-optimize": "^1.0.1", @@ -99,6 +106,9 @@ "css-loader": "^0.28.11", "docz": "^0.2.6", "docz-theme-default": "^0.2.10", + "enzyme": "^3.3.0", + "enzyme-adapter-react-16": "^1.1.1", + "enzyme-to-json": "^3.3.4", "extract-text-webpack-plugin": "^4.0.0-beta.0", "final-form": "^4.8.1", "flat": "^4.0.0", @@ -109,12 +119,18 @@ "graphql-playground-middleware-express": "^1.7.0", "html-webpack-plugin": "^3.2.0", "jest": "^23.3.0", + "jsdom": "^11.11.0", "loader-utils": "^1.1.0", "npm-run-all": "^4.1.3", + "postcss-advanced-variables": "^2.3.3", + "postcss-css-variables": "^0.9.0", "postcss-flexbugs-fixes": "^3.3.1", "postcss-font-magician": "^2.2.1", + "postcss-import": "^11.1.0", "postcss-loader": "^2.1.5", - "precss": "^3.1.2", + "postcss-nested": "^3.0.0", + "postcss-prepend-imports": "^1.0.1", + "postcss-preset-env": "^5.2.1", "prettier": "^1.13.4", "query-string": "^6.1.0", "raw-loader": "^0.5.1", @@ -123,14 +139,17 @@ "react-dom": "^16.4.0", "react-final-form": "^3.6.0", "react-relay": "github:coralproject/patched#react-relay", + "react-responsive": "^4.1.0", "react-test-renderer": "^16.4.1", "recompose": "^0.27.1", "relay-compiler": "github:coralproject/patched#relay-compiler", "relay-compiler-language-typescript": "github:coralproject/patched#relay-compiler-language-typescript", + "relay-local-schema": "^0.7.0", "relay-runtime": "github:coralproject/patched#relay-runtime", "relay-test-utils": "github:coralproject/patched#relay-test-utils", + "sinon": "^6.1.2", "style-loader": "^0.21.0", - "ts-jest": "^22.4.6", + "ts-jest": "^23.0.0", "ts-loader": "^4.3.1", "ts-node": "^6.1.0", "tsconfig-paths": "^3.4.0", diff --git a/src/core/client/.babelrc.js b/src/core/client/.babelrc.js index 6650ad9c9..02fdffd71 100644 --- a/src/core/client/.babelrc.js +++ b/src/core/client/.babelrc.js @@ -8,5 +8,8 @@ module.exports = { production: { plugins: [], }, + test: { + plugins: ["@babel/transform-modules-commonjs"], + }, }, }; diff --git a/src/core/client/framework/lib/bootstrap/withContext.tsx b/src/core/client/framework/lib/bootstrap/withContext.tsx index 82ae2695f..677da236a 100644 --- a/src/core/client/framework/lib/bootstrap/withContext.tsx +++ b/src/core/client/framework/lib/bootstrap/withContext.tsx @@ -1,5 +1,9 @@ import * as React from "react"; -import { hoistStatics, InferableComponentEnhancer } from "recompose"; +import { + hoistStatics, + InferableComponentEnhancer, + wrapDisplayName, +} from "recompose"; import { TalkContext, TalkContextConsumer } from "./TalkContext"; @@ -12,11 +16,17 @@ function withContext( propsCallback: (context: TalkContext) => T ): InferableComponentEnhancer { return hoistStatics( - (WrappedComponent: React.ComponentType) => (props: any) => ( - - {context => } - - ) + (WrappedComponent: React.ComponentType) => { + const Component: React.StatelessComponent = props => ( + + {context => ( + + )} + + ); + Component.displayName = wrapDisplayName(WrappedComponent, "withContext"); + return Component; + } ); } diff --git a/src/core/client/framework/lib/messages.tsx b/src/core/client/framework/lib/messages.tsx index c8234c670..316cd6e8a 100644 --- a/src/core/client/framework/lib/messages.tsx +++ b/src/core/client/framework/lib/messages.tsx @@ -13,7 +13,7 @@ export const VALIDATION_REQUIRED = () => ( ); export const VALIDATION_TOO_SHORT = () => ( - + This field is too short. ); diff --git a/src/core/client/framework/lib/relay/QueryRenderer.tsx b/src/core/client/framework/lib/relay/QueryRenderer.tsx index 30a42dbe0..a5845c0b6 100644 --- a/src/core/client/framework/lib/relay/QueryRenderer.tsx +++ b/src/core/client/framework/lib/relay/QueryRenderer.tsx @@ -1,24 +1,18 @@ import React, { Component } from "react"; -import { QueryRenderer } from "react-relay"; -import { CacheConfig, GraphQLTaggedNode, RerunParam } from "relay-runtime"; +import { + QueryRenderer, + QueryRendererProps as QueryRendererPropsOrig, +} from "react-relay"; + +import { Omit } from "talk-framework/types"; import { TalkContextConsumer } from "../bootstrap/TalkContext"; -// Taken from relay types and added Generic support for Variables and Response -export interface QueryRendererProps { - cacheConfig?: CacheConfig; - query?: GraphQLTaggedNode | null; - render(readyState: ReadyState): React.ReactElement | undefined | null; - variables: V; - rerunParamExperimental?: RerunParam; -} - -// Taken from relay types and added Generic support for Variables and Response -export interface ReadyState { - error: Error | undefined | null; - props: R | undefined | null; - retry?(): void; -} +// Omit environment as we are passing this from the context. +export type QueryRendererProps = Omit< + QueryRendererPropsOrig, + "environment" +>; /** * TalkQueryRenderer is a wrappper around Relay's `QueryRenderer`. diff --git a/src/core/client/framework/lib/relay/createMutationContainer.tsx b/src/core/client/framework/lib/relay/createMutationContainer.tsx index 3e3d8bf60..d0a91e92d 100644 --- a/src/core/client/framework/lib/relay/createMutationContainer.tsx +++ b/src/core/client/framework/lib/relay/createMutationContainer.tsx @@ -1,5 +1,10 @@ import * as React from "react"; -import { compose, hoistStatics, InferableComponentEnhancer } from "recompose"; +import { + compose, + hoistStatics, + InferableComponentEnhancer, + wrapDisplayName, +} from "recompose"; import { Environment } from "relay-runtime"; import { withContext } from "../bootstrap"; @@ -19,6 +24,11 @@ function createMutationContainer( withContext(({ relayEnvironment }) => ({ relayEnvironment })), hoistStatics((WrappedComponent: React.ComponentType) => { class CreateMutationContainer extends React.Component { + public static displayName = wrapDisplayName( + WrappedComponent, + "createMutationContainer" + ); + private commit = (input: I) => { return commit(this.props.relayEnvironment, input); }; diff --git a/src/core/client/framework/lib/relay/index.ts b/src/core/client/framework/lib/relay/index.ts index e55279f7c..43dca5178 100644 --- a/src/core/client/framework/lib/relay/index.ts +++ b/src/core/client/framework/lib/relay/index.ts @@ -7,6 +7,7 @@ export { default as QueryRenderer } from "./QueryRenderer"; export * from "./QueryRenderer"; export { default as createMutationContainer } from "./createMutationContainer"; export { default as createAndRetain } from "./createAndRetain"; +export { default as wrapFetchWithLogger } from "./wrapFetchWithLogger"; export { commitMutationPromise, commitMutationPromiseNormalized, diff --git a/src/core/client/framework/lib/relay/withFragmentContainer.ts b/src/core/client/framework/lib/relay/withFragmentContainer.ts index c4636fe3c..2541b2001 100644 --- a/src/core/client/framework/lib/relay/withFragmentContainer.ts +++ b/src/core/client/framework/lib/relay/withFragmentContainer.ts @@ -6,7 +6,7 @@ import { InferableComponentEnhancerWithProps } from "recompose"; * from Relay. */ export default ( - fragmentSpec: GraphQLTaggedNode + fragmentSpec: { [P in keyof T]: GraphQLTaggedNode } ): InferableComponentEnhancerWithProps => ( component: React.ComponentType ) => createFragmentContainer(component, fragmentSpec) as any; diff --git a/src/core/client/framework/lib/relay/withLocalStateContainer.tsx b/src/core/client/framework/lib/relay/withLocalStateContainer.tsx index d08aa5d55..9de4180bb 100644 --- a/src/core/client/framework/lib/relay/withLocalStateContainer.tsx +++ b/src/core/client/framework/lib/relay/withLocalStateContainer.tsx @@ -1,6 +1,11 @@ import * as React from "react"; import { compose, hoistStatics, InferableComponentEnhancer } from "recompose"; -import { CSelector, CSnapshot, Environment } from "relay-runtime"; +import { + CSelector, + CSnapshot, + Environment, + GraphQLTaggedNode, +} from "relay-runtime"; import { withContext } from "../bootstrap"; @@ -25,7 +30,7 @@ export const LOCAL_ID = "client:root.local"; * must have the `LOCAL_ID`. */ function withLocalStateContainer( - fragmentSpec: any + fragmentSpec: GraphQLTaggedNode ): InferableComponentEnhancer<{ local: T }> { return compose( withContext(({ relayEnvironment }) => ({ relayEnvironment })), @@ -33,7 +38,7 @@ function withLocalStateContainer( class LocalStateContainer extends React.Component { constructor(props: Props) { super(props); - const fragment = fragmentSpec.data().default; + const fragment = (fragmentSpec as any).data().default; if (fragment.kind !== "Fragment") { throw new Error("Expected fragment"); } diff --git a/src/core/client/framework/lib/relay/withPaginationContainer.ts b/src/core/client/framework/lib/relay/withPaginationContainer.ts index 5f768c0a7..bb2740d01 100644 --- a/src/core/client/framework/lib/relay/withPaginationContainer.ts +++ b/src/core/client/framework/lib/relay/withPaginationContainer.ts @@ -10,9 +10,13 @@ import { InferableComponentEnhancerWithProps } from "recompose"; * withPaginationContainer is a curried version of `createPaginationContainers` * from Relay. */ -export default ( - fragmentSpec: GraphQLTaggedNode, - connectionConfig: ConnectionConfig +export default ( + fragmentSpec: { [P in keyof T]: GraphQLTaggedNode }, + connectionConfig: ConnectionConfig< + InnerProps, + FragmentVariables, + QueryVariables + > ): InferableComponentEnhancerWithProps< T & { relay: RelayPaginationProp }, { [P in keyof T]: any } diff --git a/src/core/client/framework/lib/relay/withRefetchContainer.ts b/src/core/client/framework/lib/relay/withRefetchContainer.ts index 7d11e0e16..9e9989758 100644 --- a/src/core/client/framework/lib/relay/withRefetchContainer.ts +++ b/src/core/client/framework/lib/relay/withRefetchContainer.ts @@ -10,7 +10,7 @@ import { InferableComponentEnhancerWithProps } from "recompose"; * from Relay. */ export default ( - fragmentSpec: GraphQLTaggedNode, + fragmentSpec: { [P in keyof T]: GraphQLTaggedNode }, refetchQuery: GraphQLTaggedNode ): InferableComponentEnhancerWithProps< T & { relay: RelayRefetchProp }, diff --git a/src/core/client/framework/lib/relay/wrapFetchWithLogger.ts b/src/core/client/framework/lib/relay/wrapFetchWithLogger.ts new file mode 100644 index 000000000..d320e8ab0 --- /dev/null +++ b/src/core/client/framework/lib/relay/wrapFetchWithLogger.ts @@ -0,0 +1,25 @@ +import { FetchFunction } from "relay-runtime"; + +/** + * Decorates the fetch function with error logging. + * Intended for testing purposes. + */ +export default function wrapFetchWithLogger( + fetch: FetchFunction, + logResult?: boolean +): FetchFunction { + return async (...args: any[]) => { + try { + const result = await (fetch as any)(...args); + if (logResult) { + // tslint:disable-next-line:no-console + console.log(JSON.stringify(result)); + } + return result; + } catch (err) { + // tslint:disable-next-line:no-console + console.error(err); + throw err; + } + }; +} diff --git a/src/core/client/stream/.babelrc.js b/src/core/client/stream/.babelrc.js index d2601c757..11bf9a670 100644 --- a/src/core/client/stream/.babelrc.js +++ b/src/core/client/stream/.babelrc.js @@ -1,5 +1,4 @@ const path = require("path"); - module.exports = { extends: "../.babelrc.js", plugins: [ diff --git a/src/core/client/stream/components/App.tsx b/src/core/client/stream/components/App.tsx index 56bdf54a2..1dd3ce496 100644 --- a/src/core/client/stream/components/App.tsx +++ b/src/core/client/stream/components/App.tsx @@ -1,38 +1,20 @@ import * as React from "react"; import { StatelessComponent } from "react"; -import { Center } from "talk-ui/components"; +import { Flex } from "talk-ui/components"; -import AssetListContainer from "../containers/AssetListContainer"; -import PostCommentFormContainer from "../containers/PostCommentFormContainer"; import StreamContainer from "../containers/StreamContainer"; -import Logo from "./Logo"; export interface AppProps { - assets?: any | null; - asset?: { - id: string; - isClosed: boolean; - comments: any | null; - } | null; - comment?: any | null; + asset: {} | null; } const App: StatelessComponent = props => { - console.log(props); - if (props.comment) { - return
Comment
; - } - if (props.assets) { - return ; - } if (props.asset) { return ( -
- - - -
+ + + ); } return
Asset not found
; diff --git a/src/core/client/stream/components/AssetList.tsx b/src/core/client/stream/components/AssetList.tsx deleted file mode 100644 index a82c34fe6..000000000 --- a/src/core/client/stream/components/AssetList.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from "react"; -import { StatelessComponent } from "react"; - -export interface AssetListProps { - assets: ReadonlyArray<{ id: string; title: string | null }>; -} - -const AssetList: StatelessComponent = props => { - return ( -
- {props.assets.map(asset =>
{asset.title}
)} -
- ); -}; - -export default AssetList; diff --git a/src/core/client/stream/components/Comment.css b/src/core/client/stream/components/Comment.css index 4e23b2d02..77dc9d88d 100644 --- a/src/core/client/stream/components/Comment.css +++ b/src/core/client/stream/components/Comment.css @@ -1,11 +1,10 @@ .root { - width: 400px; } .gutterBottom { - margin-bottom: calc(2 px * $spacing-unit); + margin-bottom: calc(1px * var(--spacing-unit)); } -.author { - font-weight: $font-weight-medium; +.topBar { + margin-bottom: calc(0.5px * var(--spacing-unit)); } diff --git a/src/core/client/stream/components/Comment.spec.tsx b/src/core/client/stream/components/Comment.spec.tsx index 4f7448a93..9bad32868 100644 --- a/src/core/client/stream/components/Comment.spec.tsx +++ b/src/core/client/stream/components/Comment.spec.tsx @@ -1,5 +1,6 @@ +import { shallow } from "enzyme"; import React from "react"; -import { createRenderer } from "react-test-renderer/shallow"; + import Comment from "./Comment"; it("renders username and body", () => { @@ -10,9 +11,8 @@ it("renders username and body", () => { }, body: "Woof", }; - const renderer = createRenderer(); - renderer.render(); - expect(renderer.getRenderOutput()).toMatchSnapshot(); + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); }); it("renders with gutterBottom", () => { @@ -24,7 +24,6 @@ it("renders with gutterBottom", () => { body: "Woof", gutterBottom: true, }; - const renderer = createRenderer(); - renderer.render(); - expect(renderer.getRenderOutput()).toMatchSnapshot(); + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); }); diff --git a/src/core/client/stream/components/Comment.tsx b/src/core/client/stream/components/Comment.tsx index a332b42a3..1c092d19c 100644 --- a/src/core/client/stream/components/Comment.tsx +++ b/src/core/client/stream/components/Comment.tsx @@ -4,6 +4,7 @@ import { StatelessComponent } from "react"; import { Typography } from "talk-ui/components"; import * as styles from "./Comment.css"; import PermalinkPopover from "./PermalinkPopover"; +import Username from "./Username"; export interface CommentProps { id: string; @@ -15,17 +16,15 @@ export interface CommentProps { gutterBottom?: boolean; } -// make a permalink popover - const Comment: StatelessComponent = props => { const rootClassName = cn(styles.root, props.className, { [styles.gutterBottom]: props.gutterBottom, }); return ( -
- - {props.author && props.author.username} - +
+
+ {props.author && {props.author.username}} +
{props.body}
diff --git a/src/core/client/stream/components/Indent.css b/src/core/client/stream/components/Indent.css new file mode 100644 index 000000000..19100de68 --- /dev/null +++ b/src/core/client/stream/components/Indent.css @@ -0,0 +1,8 @@ +.root { + border-left: 3px solid; + padding-left: calc(1px * var(--spacing-unit)); +} + +.level0 { + border-color: var(--palette-secondary-darkest); +} diff --git a/src/core/client/stream/components/Indent.tsx b/src/core/client/stream/components/Indent.tsx new file mode 100644 index 000000000..7ea8fddf3 --- /dev/null +++ b/src/core/client/stream/components/Indent.tsx @@ -0,0 +1,15 @@ +import cn from "classnames"; +import React, { StatelessComponent } from "react"; + +import * as styles from "./Indent.css"; + +export interface IndentProps { + level?: number; + children: React.ReactNode; +} + +const Indent: StatelessComponent = props => { + return
{props.children}
; +}; + +export default Indent; diff --git a/src/core/client/stream/components/PostCommentForm.css b/src/core/client/stream/components/PostCommentForm.css index a536dcc8b..2e9cdcc9e 100644 --- a/src/core/client/stream/components/PostCommentForm.css +++ b/src/core/client/stream/components/PostCommentForm.css @@ -3,10 +3,12 @@ display: block; height: 100px; - width: 400px; - margin-bottom: calc(2px * $spacing-unit); + width: 100%; + box-sizing: border-box; + margin-bottom: calc(1px * var(--spacing-unit)); } -.postButton { - float: right; +.postButtonContainer { + display: flex; + justify-content: flex-end; } diff --git a/src/core/client/stream/components/PostCommentForm.tsx b/src/core/client/stream/components/PostCommentForm.tsx index 2187994be..09db70e39 100644 --- a/src/core/client/stream/components/PostCommentForm.tsx +++ b/src/core/client/stream/components/PostCommentForm.tsx @@ -39,11 +39,13 @@ const PostCommentForm: StatelessComponent = props => (
)} - - - +
+ + + +
)} diff --git a/src/core/client/stream/components/ReplyList.spec.tsx b/src/core/client/stream/components/ReplyList.spec.tsx new file mode 100644 index 000000000..12b673732 --- /dev/null +++ b/src/core/client/stream/components/ReplyList.spec.tsx @@ -0,0 +1,40 @@ +import { shallow } from "enzyme"; +import { noop } from "lodash"; +import React from "react"; +import sinon, { SinonSpy } from "sinon"; + +import ReplyList, { ReplyListProps } from "./ReplyList"; + +it("renders correctly", () => { + const props: ReplyListProps = { + commentID: "comment-id", + comments: [{ id: "comment-1" }, { id: "comment-2" }], + onShowAll: noop, + hasMore: false, + disableShowAll: false, + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +describe("when there is more", () => { + const props: ReplyListProps = { + commentID: "comment-id", + comments: [{ id: "comment-1" }, { id: "comment-2" }], + onShowAll: sinon.spy(), + hasMore: true, + disableShowAll: false, + }; + + const wrapper = shallow(); + it("renders a load more button", () => { + expect(wrapper).toMatchSnapshot(); + }); + + it("calls onLoadMore", () => { + wrapper + .find("#talk-comments-replyList-showAll--comment-id") + .simulate("click"); + expect((props.onShowAll as SinonSpy).calledOnce).toBe(true); + }); +}); diff --git a/src/core/client/stream/components/ReplyList.tsx b/src/core/client/stream/components/ReplyList.tsx new file mode 100644 index 000000000..ec366771c --- /dev/null +++ b/src/core/client/stream/components/ReplyList.tsx @@ -0,0 +1,45 @@ +import { Localized } from "fluent-react/compat"; +import * as React from "react"; +import { StatelessComponent } from "react"; + +import { Button } from "talk-ui/components"; + +import CommentContainer from "../containers/CommentContainer"; +import Indent from "./Indent"; + +export interface ReplyListProps { + commentID: string; + comments: ReadonlyArray<{ id: string }>; + onShowAll: () => void; + hasMore: boolean; + disableShowAll: boolean; +} + +const ReplyList: StatelessComponent = props => { + return ( + +
+ {props.comments.map(comment => ( + + ))} + {props.hasMore && ( + + + + )} +
+
+ ); +}; + +export default ReplyList; diff --git a/src/core/client/stream/components/Stream.css b/src/core/client/stream/components/Stream.css new file mode 100644 index 000000000..13c90d031 --- /dev/null +++ b/src/core/client/stream/components/Stream.css @@ -0,0 +1,4 @@ +.root { + width: 100%; + max-width: 400px; +} diff --git a/src/core/client/stream/components/Stream.spec.tsx b/src/core/client/stream/components/Stream.spec.tsx new file mode 100644 index 000000000..05d30d66e --- /dev/null +++ b/src/core/client/stream/components/Stream.spec.tsx @@ -0,0 +1,45 @@ +import { shallow } from "enzyme"; +import { noop } from "lodash"; +import React from "react"; +import sinon from "sinon"; + +import Stream, { StreamProps } from "./Stream"; + +it("renders correctly", () => { + const props: StreamProps = { + assetID: "asset-id", + isClosed: false, + comments: [{ id: "comment-1" }, { id: "comment-2" }], + onLoadMore: noop, + disableLoadMore: false, + hasMore: false, + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +describe("when there is more", () => { + const props = { + assetID: "asset-id", + isClosed: false, + comments: [{ id: "comment-1" }, { id: "comment-2" }], + onLoadMore: sinon.spy(), + disableLoadMore: false, + hasMore: true, + }; + + const wrapper = shallow(); + it("renders a load more button", () => { + expect(wrapper).toMatchSnapshot(); + }); + + it("calls onLoadMore", () => { + wrapper.find("#talk-comments-stream-loadMore").simulate("click"); + expect(props.onLoadMore.calledOnce).toBe(true); + }); + + const wrapperDisabledButton = shallow(); + it("disables load more button", () => { + expect(wrapperDisabledButton).toMatchSnapshot(); + }); +}); diff --git a/src/core/client/stream/components/Stream.tsx b/src/core/client/stream/components/Stream.tsx index c42bb21b3..f6d39940e 100644 --- a/src/core/client/stream/components/Stream.tsx +++ b/src/core/client/stream/components/Stream.tsx @@ -1,18 +1,52 @@ +import { Localized } from "fluent-react/compat"; import * as React from "react"; import { StatelessComponent } from "react"; +import { Button } from "talk-ui/components"; + import CommentContainer from "../containers/CommentContainer"; +import PostCommentFormContainer from "../containers/PostCommentFormContainer"; +import ReplyListContainer from "../containers/ReplyListContainer"; +import Logo from "./Logo"; +import * as styles from "./Stream.css"; export interface StreamProps { + assetID: string; + isClosed: boolean; comments: ReadonlyArray<{ id: string }>; + onLoadMore: () => void; + hasMore: boolean; + disableLoadMore: boolean; } const Stream: StatelessComponent = props => { return ( -
- {props.comments.map(comment => ( - - ))} +
+ + +
+ {props.comments.map(comment => ( +
+ + +
+ ))} + {props.hasMore && ( + + + + )} +
); }; diff --git a/src/core/client/stream/components/Username.css b/src/core/client/stream/components/Username.css new file mode 100644 index 000000000..9cb1baa15 --- /dev/null +++ b/src/core/client/stream/components/Username.css @@ -0,0 +1,3 @@ +.root { + composes: heading4 from "talk-ui/shared/typography.css"; +} diff --git a/src/core/client/stream/components/Username.spec.tsx b/src/core/client/stream/components/Username.spec.tsx new file mode 100644 index 000000000..c4f1c1dfe --- /dev/null +++ b/src/core/client/stream/components/Username.spec.tsx @@ -0,0 +1,12 @@ +import { shallow } from "enzyme"; +import React from "react"; + +import Username from "./Username"; + +it("renders correctly", () => { + const props = { + children: "Marvin", + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src/core/client/stream/components/Username.tsx b/src/core/client/stream/components/Username.tsx new file mode 100644 index 000000000..117ebdb3d --- /dev/null +++ b/src/core/client/stream/components/Username.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { StatelessComponent } from "react"; + +import * as styles from "./Username.css"; + +export interface CommentProps { + children: string; +} + +const Username: StatelessComponent = props => { + return {props.children}; +}; + +export default Username; diff --git a/src/core/client/stream/components/__snapshots__/Comment.spec.tsx.snap b/src/core/client/stream/components/__snapshots__/Comment.spec.tsx.snap index 4b009e257..9aafeaf5c 100644 --- a/src/core/client/stream/components/__snapshots__/Comment.spec.tsx.snap +++ b/src/core/client/stream/components/__snapshots__/Comment.spec.tsx.snap @@ -2,14 +2,16 @@ exports[`renders username and body 1`] = `
- - Marvin - + + Marvin + +
Woof @@ -18,14 +20,16 @@ exports[`renders username and body 1`] = ` exports[`renders with gutterBottom 1`] = `
- - Marvin - + + Marvin + +
Woof diff --git a/src/core/client/stream/components/__snapshots__/ReplyList.spec.tsx.snap b/src/core/client/stream/components/__snapshots__/ReplyList.spec.tsx.snap new file mode 100644 index 000000000..36fb0c110 --- /dev/null +++ b/src/core/client/stream/components/__snapshots__/ReplyList.spec.tsx.snap @@ -0,0 +1,72 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +
+ + +
+
+`; + +exports[`when there is more renders a load more button 1`] = ` + +
+ + + + + Show All Replies + + +
+
+`; diff --git a/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap b/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap new file mode 100644 index 000000000..c529ea1ad --- /dev/null +++ b/src/core/client/stream/components/__snapshots__/Stream.spec.tsx.snap @@ -0,0 +1,202 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+ + +
+
+ + +
+
+ + +
+
+
+`; + +exports[`when there is more disables load more button 1`] = ` +
+ + +
+
+ + +
+
+ + +
+ + + Load More + + +
+
+`; + +exports[`when there is more renders a load more button 1`] = ` +
+ + +
+
+ + +
+
+ + +
+ + + Load More + + +
+
+`; diff --git a/src/core/client/stream/components/__snapshots__/Username.spec.tsx.snap b/src/core/client/stream/components/__snapshots__/Username.spec.tsx.snap new file mode 100644 index 000000000..27d624f2f --- /dev/null +++ b/src/core/client/stream/components/__snapshots__/Username.spec.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + + Marvin + +`; diff --git a/src/core/client/stream/containers/AppContainer.tsx b/src/core/client/stream/containers/AppContainer.tsx index 02abbf314..8b84660ca 100644 --- a/src/core/client/stream/containers/AppContainer.tsx +++ b/src/core/client/stream/containers/AppContainer.tsx @@ -15,26 +15,16 @@ const AppContainer: StatelessComponent = props => { return ; }; -const enhanced = withFragmentContainer<{ data: Data }>( - graphql` +const enhanced = withFragmentContainer<{ data: Data }>({ + data: graphql` fragment AppContainer on Query - @argumentDefinitions( - assetID: { type: "ID!" } - showAssetList: { type: "Boolean!" } - ) { - assets @include(if: $showAssetList) { - ...AssetListContainer_assets - } - asset(id: $assetID) @skip(if: $showAssetList) { - id - isClosed - comments { - ...StreamContainer_comments - } + @argumentDefinitions(assetID: { type: "ID!" }) { + asset(id: $assetID) { + ...StreamContainer_asset } } - ` -)(AppContainer); + `, +})(AppContainer); export type AppContainerProps = PropTypesOf; export default enhanced; diff --git a/src/core/client/stream/containers/AssetListContainer.tsx b/src/core/client/stream/containers/AssetListContainer.tsx deleted file mode 100644 index 6e441f00e..000000000 --- a/src/core/client/stream/containers/AssetListContainer.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { StatelessComponent } from "react"; -import { graphql } from "react-relay"; - -import { withFragmentContainer } from "talk-framework/lib/relay"; -import { PropTypesOf } from "talk-framework/types"; -import { AssetListContainer_assets as Data } from "talk-stream/__generated__/AssetListContainer_assets.graphql"; - -import AssetList from "../components/AssetList"; - -interface InnerProps { - assets: Data; -} - -const AssetListContainer: StatelessComponent = props => { - const assets = props.assets.edges.map(edge => edge.node); - return ; -}; - -const enhanced = withFragmentContainer<{ assets: Data }>( - graphql` - fragment AssetListContainer_assets on AssetsConnection { - edges { - node { - id - title - } - } - } - ` -)(AssetListContainer); - -export type AssetListContainerProps = PropTypesOf; -export default enhanced; diff --git a/src/core/client/stream/containers/CommentContainer.spec.tsx b/src/core/client/stream/containers/CommentContainer.spec.tsx new file mode 100644 index 000000000..94ffb7392 --- /dev/null +++ b/src/core/client/stream/containers/CommentContainer.spec.tsx @@ -0,0 +1,18 @@ +import { shallow } from "enzyme"; +import React from "react"; + +import { CommentContainer } from "./CommentContainer"; + +it("renders username and body", () => { + const props = { + data: { + author: { + username: "Marvin", + }, + body: "Woof", + }, + }; + + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src/core/client/stream/containers/CommentContainer.tsx b/src/core/client/stream/containers/CommentContainer.tsx index 3d7956dc6..9f20ecfc5 100644 --- a/src/core/client/stream/containers/CommentContainer.tsx +++ b/src/core/client/stream/containers/CommentContainer.tsx @@ -9,22 +9,28 @@ import Comment, { CommentProps } from "../components/Comment"; type InnerProps = { data: Data } & Omit; -const CommentContainer: StatelessComponent = props => { +// tslint:disable-next-line:no-unused-expression +graphql` + fragment CommentContainer_comment on Comment { + author { + username + } + body + } +`; + +export const CommentContainer: StatelessComponent = props => { const { data, ...rest } = props; return ; }; -const enhanced = withFragmentContainer<{ data: Data }>( - graphql` +const enhanced = withFragmentContainer<{ data: Data }>({ + data: graphql` fragment CommentContainer on Comment { - id - author { - username - } - body + ...CommentContainer_comment @relay(mask: false) } - ` -)(CommentContainer); + `, +})(CommentContainer); export type CommentContainerProps = PropTypesOf; export default enhanced; diff --git a/src/core/client/stream/containers/PostCommentFormContainer.tsx b/src/core/client/stream/containers/PostCommentFormContainer.tsx index d2da26f64..bf4ab8cf9 100644 --- a/src/core/client/stream/containers/PostCommentFormContainer.tsx +++ b/src/core/client/stream/containers/PostCommentFormContainer.tsx @@ -25,6 +25,8 @@ class PostCommentFormContainer extends Component { if (error instanceof BadUserInputError) { return error.invalidArgsLocalized; } + // tslint:disable-next-line:no-console + console.error(error); } return undefined; }; diff --git a/src/core/client/stream/containers/ReplyListContainer.spec.tsx b/src/core/client/stream/containers/ReplyListContainer.spec.tsx new file mode 100644 index 000000000..75cb129ca --- /dev/null +++ b/src/core/client/stream/containers/ReplyListContainer.spec.tsx @@ -0,0 +1,86 @@ +import { shallow, ShallowWrapper } from "enzyme"; +import { noop } from "lodash"; +import React from "react"; + +import { PropTypesOf } from "talk-framework/types"; + +import ReplyList from "../components/ReplyList"; +import { ReplyListContainer } from "./ReplyListContainer"; + +it("renders correctly", () => { + const props: any = { + comment: { + id: "comment-id", + replies: { + edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }], + }, + }, + relay: { + hasMore: noop, + isLoading: noop, + }, + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +it("renders correctly when replies are null", () => { + const props: any = { + comment: { + id: "comment-id", + replies: null, + }, + relay: { + hasMore: noop, + isLoading: noop, + }, + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +describe("when has more replies", () => { + let finishLoading: ((error?: Error) => void) | null = null; + const props: PropTypesOf = { + comment: { + id: "comment-id", + replies: { + edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }], + }, + }, + relay: { + hasMore: () => true, + isLoading: () => false, + loadMore: (_: any, callback: () => void) => (finishLoading = callback), + } as any, + }; + + let wrapper: ShallowWrapper; + + beforeAll(() => (wrapper = shallow())); + + it("renders hasMore", () => { + expect(wrapper).toMatchSnapshot(); + }); + + describe("when showing all", () => { + beforeAll(() => { + wrapper + .find(ReplyList) + .props() + .onShowAll(); + }); + it("calls relay loadMore", () => { + expect(finishLoading).not.toBeNull(); + }); + it("disables show all button", () => { + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + }); + it("enable show all button after loading is done", () => { + finishLoading!(); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/src/core/client/stream/containers/ReplyListContainer.tsx b/src/core/client/stream/containers/ReplyListContainer.tsx new file mode 100644 index 000000000..d147edb1c --- /dev/null +++ b/src/core/client/stream/containers/ReplyListContainer.tsx @@ -0,0 +1,132 @@ +import React from "react"; +import { graphql, RelayPaginationProp } from "react-relay"; + +import { withPaginationContainer } from "talk-framework/lib/relay"; +import { PropTypesOf } from "talk-framework/types"; +import { ReplyListContainer_comment as Data } from "talk-stream/__generated__/ReplyListContainer_comment.graphql"; +import { + COMMENT_SORT, + ReplyListContainerPaginationQueryVariables, +} from "talk-stream/__generated__/ReplyListContainerPaginationQuery.graphql"; + +import ReplyList from "../components/ReplyList"; + +export interface InnerProps { + comment: Data; + relay: RelayPaginationProp; +} + +export class ReplyListContainer extends React.Component { + public state = { + disableShowAll: false, + }; + + public render() { + if (this.props.comment.replies === null) { + return null; + } + const comments = this.props.comment.replies.edges.map(edge => edge.node); + return ( + + ); + } + + private showAll = () => { + if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { + return; + } + + this.setState({ disableShowAll: true }); + this.props.relay.loadMore( + 999999999, // Fetch All Replies + error => { + this.setState({ disableShowAll: false }); + if (error) { + // tslint:disable-next-line:no-console + console.error(error); + } + } + ); + }; +} + +// TODO: (cvle) This should be autogenerated. +interface FragmentVariables { + count: number; + cursor?: string; + orderBy: COMMENT_SORT; +} + +const enhanced = withPaginationContainer< + { comment: Data }, + InnerProps, + FragmentVariables, + ReplyListContainerPaginationQueryVariables +>( + { + comment: graphql` + fragment ReplyListContainer_comment on Comment + @argumentDefinitions( + count: { type: "Int!", defaultValue: 5 } + cursor: { type: "Cursor" } + orderBy: { type: "COMMENT_SORT!", defaultValue: CREATED_AT_ASC } + ) { + id + replies(first: $count, after: $cursor, orderBy: $orderBy) + @connection(key: "ReplyList_replies") { + edges { + node { + id + ...CommentContainer + } + } + } + } + `, + }, + { + direction: "forward", + getConnectionFromProps(props) { + return props.comment && props.comment.replies; + }, + // This is also the default implementation of `getFragmentVariables` if it isn't provided. + getFragmentVariables(prevVars, totalCount) { + return { + ...prevVars, + count: totalCount, + }; + }, + getVariables(props, { count, cursor }, fragmentVariables) { + return { + count, + cursor, + orderBy: fragmentVariables.orderBy, + commentID: props.comment.id, + }; + }, + query: graphql` + # Pagination query to be fetched upon calling 'loadMore'. + # Notice that we re-use our fragment, and the shape of this query matches our fragment spec. + query ReplyListContainerPaginationQuery( + $count: Int! + $cursor: Cursor + $orderBy: COMMENT_SORT! + $commentID: ID! + ) { + comment(id: $commentID) { + ...ReplyListContainer_comment + @arguments(count: $count, cursor: $cursor, orderBy: $orderBy) + } + } + `, + } +)(ReplyListContainer); + +export type ReplyListContainerProps = PropTypesOf; +export default enhanced; diff --git a/src/core/client/stream/containers/StreamContainer.spec.tsx b/src/core/client/stream/containers/StreamContainer.spec.tsx new file mode 100644 index 000000000..2e19bda86 --- /dev/null +++ b/src/core/client/stream/containers/StreamContainer.spec.tsx @@ -0,0 +1,73 @@ +import { shallow, ShallowWrapper } from "enzyme"; +import { noop } from "lodash"; +import React from "react"; + +import { PropTypesOf } from "talk-framework/types"; + +import Stream from "../components/Stream"; +import { StreamContainer } from "./StreamContainer"; + +it("renders correctly", () => { + const props: PropTypesOf = { + asset: { + id: "asset-id", + isClosed: false, + comments: { + edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }], + }, + }, + relay: { + hasMore: noop, + isLoading: noop, + } as any, + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +describe("when has more comments", () => { + let finishLoading: ((error?: Error) => void) | null = null; + const props: PropTypesOf = { + asset: { + id: "asset-id", + isClosed: false, + comments: { + edges: [{ node: { id: "comment-1" } }, { node: { id: "comment-2" } }], + }, + }, + relay: { + hasMore: () => true, + isLoading: () => false, + loadMore: (_: any, callback: () => void) => (finishLoading = callback), + } as any, + }; + + let wrapper: ShallowWrapper; + + beforeAll(() => (wrapper = shallow())); + + it("renders hasMore", () => { + expect(wrapper).toMatchSnapshot(); + }); + + describe("when loading more", () => { + beforeAll(() => { + wrapper + .find(Stream) + .props() + .onLoadMore(); + }); + it("calls relay loadMore", () => { + expect(finishLoading).not.toBeNull(); + }); + it("disables load more button", () => { + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + }); + it("enable load more button after loading is done", () => { + finishLoading!(); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/src/core/client/stream/containers/StreamContainer.tsx b/src/core/client/stream/containers/StreamContainer.tsx index 771c25efa..d81319deb 100644 --- a/src/core/client/stream/containers/StreamContainer.tsx +++ b/src/core/client/stream/containers/StreamContainer.tsx @@ -1,32 +1,132 @@ -import React, { StatelessComponent } from "react"; -import { graphql } from "react-relay"; +import React from "react"; +import { graphql, RelayPaginationProp } from "react-relay"; -import { withFragmentContainer } from "talk-framework/lib/relay"; +import { withPaginationContainer } from "talk-framework/lib/relay"; import { PropTypesOf } from "talk-framework/types"; -import { StreamContainer_comments as Data } from "talk-stream/__generated__/StreamContainer_comments.graphql"; +import { StreamContainer_asset as Data } from "talk-stream/__generated__/StreamContainer_asset.graphql"; +import { + COMMENT_SORT, + StreamContainerPaginationQueryVariables, +} from "talk-stream/__generated__/StreamContainerPaginationQuery.graphql"; import Stream from "../components/Stream"; interface InnerProps { - comments: Data; + asset: Data; + relay: RelayPaginationProp; } -const StreamContainer: StatelessComponent = props => { - const comments = props.comments.edges.map(edge => edge.node); - return ; -}; +export class StreamContainer extends React.Component { + public state = { + disableLoadMore: false, + }; -const enhanced = withFragmentContainer<{ comments: Data }>( - graphql` - fragment StreamContainer_comments on CommentsConnection { - edges { - node { - id - ...CommentContainer + public render() { + const comments = this.props.asset.comments.edges.map(edge => edge.node); + return ( + + ); + } + + private loadMore = () => { + if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { + return; + } + this.setState({ disableLoadMore: true }); + this.props.relay.loadMore( + 10, // Fetch the next 10 feed items + error => { + this.setState({ disableLoadMore: false }); + if (error) { + // tslint:disable-next-line:no-console + console.error(error); } } - } - ` + ); + }; +} + +// TODO: (cvle) This should be autogenerated. +interface FragmentVariables { + count: number; + cursor?: string; + orderBy: COMMENT_SORT; +} + +const enhanced = withPaginationContainer< + { asset: Data }, + InnerProps, + FragmentVariables, + StreamContainerPaginationQueryVariables +>( + { + asset: graphql` + fragment StreamContainer_asset on Asset + @argumentDefinitions( + count: { type: "Int!", defaultValue: 5 } + cursor: { type: "Cursor" } + orderBy: { type: "COMMENT_SORT!", defaultValue: CREATED_AT_DESC } + ) { + id + isClosed + comments(first: $count, after: $cursor, orderBy: $orderBy) + @connection(key: "Stream_comments") { + edges { + node { + id + ...CommentContainer + ...ReplyListContainer_comment + } + } + } + } + `, + }, + { + direction: "forward", + getConnectionFromProps(props) { + return props.asset && props.asset.comments; + }, + // This is also the default implementation of `getFragmentVariables` if it isn't provided. + getFragmentVariables(prevVars, totalCount) { + return { + ...prevVars, + count: totalCount, + }; + }, + getVariables(props, { count, cursor }, fragmentVariables) { + return { + count, + cursor, + orderBy: fragmentVariables.orderBy, + // assetID isn't specified as an @argument for the fragment, but it should be a + // variable available for the fragment under the query root. + assetID: props.asset.id, + }; + }, + query: graphql` + # Pagination query to be fetched upon calling 'loadMore'. + # Notice that we re-use our fragment, and the shape of this query matches our fragment spec. + query StreamContainerPaginationQuery( + $count: Int! + $cursor: Cursor + $orderBy: COMMENT_SORT! + $assetID: ID! + ) { + asset(id: $assetID) { + ...StreamContainer_asset + @arguments(count: $count, cursor: $cursor, orderBy: $orderBy) + } + } + `, + } )(StreamContainer); export type StreamContainerProps = PropTypesOf; diff --git a/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap new file mode 100644 index 000000000..6ce053179 --- /dev/null +++ b/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders username and body 1`] = ` + +`; diff --git a/src/core/client/stream/containers/__snapshots__/ReplyListContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/ReplyListContainer.spec.tsx.snap new file mode 100644 index 000000000..52aa9c57b --- /dev/null +++ b/src/core/client/stream/containers/__snapshots__/ReplyListContainer.spec.tsx.snap @@ -0,0 +1,78 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; + +exports[`renders correctly when replies are null 1`] = `""`; + +exports[`when has more replies renders hasMore 1`] = ` + +`; + +exports[`when has more replies when showing all disables show all button 1`] = ` + +`; + +exports[`when has more replies when showing all enable show all button after loading is done 1`] = ` + +`; diff --git a/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap new file mode 100644 index 000000000..e3813397c --- /dev/null +++ b/src/core/client/stream/containers/__snapshots__/StreamContainer.spec.tsx.snap @@ -0,0 +1,80 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; + +exports[`when has more comments renders hasMore 1`] = ` + +`; + +exports[`when has more comments when loading more disables load more button 1`] = ` + +`; + +exports[`when has more comments when loading more enable load more button after loading is done 1`] = ` + +`; diff --git a/src/core/client/stream/queries/AppQuery.spec.tsx b/src/core/client/stream/queries/AppQuery.spec.tsx new file mode 100644 index 000000000..a301a76b7 --- /dev/null +++ b/src/core/client/stream/queries/AppQuery.spec.tsx @@ -0,0 +1,31 @@ +import { shallow } from "enzyme"; +import React from "react"; + +import { render } from "./AppQuery"; + +it("renders app", () => { + const data = { + props: {} as any, + error: null, + }; + const wrapper = shallow(React.createElement(() => render(data))); + expect(wrapper).toMatchSnapshot(); +}); + +it("renders loading", () => { + const data = { + props: null, + error: null, + }; + const wrapper = shallow(React.createElement(() => render(data))); + expect(wrapper).toMatchSnapshot(); +}); + +it("renders error", () => { + const data = { + props: null, + error: new Error("error"), + }; + const wrapper = shallow(React.createElement(() => render(data))); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src/core/client/stream/queries/AppQuery.tsx b/src/core/client/stream/queries/AppQuery.tsx index d1256d249..3fe95b6d9 100644 --- a/src/core/client/stream/queries/AppQuery.tsx +++ b/src/core/client/stream/queries/AppQuery.tsx @@ -1,10 +1,10 @@ import * as React from "react"; import { StatelessComponent } from "react"; +import { ReadyState } from "react-relay"; import { graphql, QueryRenderer, - ReadyState, withLocalStateContainer, } from "talk-framework/lib/relay"; import { @@ -15,7 +15,7 @@ import { AppQueryLocal as Local } from "talk-stream/__generated__/AppQueryLocal. import AppContainer from "../containers/AppContainer"; -const render = ({ error, props }: ReadyState) => { +export const render = ({ error, props }: ReadyState) => { if (error) { return
{error.message}
; } @@ -33,16 +33,12 @@ const AppQuery: StatelessComponent = props => { return ( query={graphql` - query AppQuery($assetID: ID!, $showAssetList: Boolean!) { - ...AppContainer - @arguments(assetID: $assetID, showAssetList: $showAssetList) + query AppQuery($assetID: ID!) { + ...AppContainer @arguments(assetID: $assetID) } `} variables={{ - // We cast `null` to any due to restrictions of the current graphql syntax. - assetID: props.local.assetID || (null as any), - // TODO: This is set to false, as server does not support querying assets yet. - showAssetList: !props.local.assetID && false, + assetID: props.local.assetID, }} render={render} /> diff --git a/src/core/client/stream/queries/__snapshots__/AppQuery.spec.tsx.snap b/src/core/client/stream/queries/__snapshots__/AppQuery.spec.tsx.snap new file mode 100644 index 000000000..dbd2ad45a --- /dev/null +++ b/src/core/client/stream/queries/__snapshots__/AppQuery.spec.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders app 1`] = ` + +`; + +exports[`renders error 1`] = ` +
+ error +
+`; + +exports[`renders loading 1`] = ` +
+ Loading +
+`; diff --git a/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap b/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap new file mode 100644 index 000000000..312540c09 --- /dev/null +++ b/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap @@ -0,0 +1,214 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`loads more comments 1`] = ` +
+
+

+ Talk NEO +

+
+
+