diff --git a/.eslintrc.js b/.eslintrc.js
index 4860e5898..7ade414c8 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -29,6 +29,7 @@ module.exports = {
'@typescript-eslint',
'react',
'react-hooks',
+ 'sort-destructure-keys'
// 'jest'
],
rules: {
@@ -42,7 +43,9 @@ module.exports = {
}],
'arrow-body-style': 'off',
'arrow-parens': 'off',
- 'camelcase': 'warn',
+ 'camelcase': ['warn', {
+ allow: ["^UNSAFE_", "doc_count", "^active_"],
+ }],
'comma-dangle': ['warn', 'always-multiline'],
'func-names': ['warn', 'as-needed'],
'function-paren-newline': ['warn', 'consistent'],
@@ -88,6 +91,9 @@ module.exports = {
commentPattern: 'break[\\s\\w]*omitted',
}],
'no-nested-ternary': 'off',
+ 'no-unused-expressions': ['warn', {
+ allowShortCircuit: true,
+ }],
'no-var': 'error', // Must use const or let.
'object-property-newline': ['warn', {
// allowAllPropertiesOnSameLine: false,
@@ -173,6 +179,9 @@ module.exports = {
'react/sort-comp': 'warn',
'react/sort-prop-types': 'error',
'react/prop-types': 'off', // Disable prop-types as TS is used for type checking.
+ 'sort-destructure-keys/sort-destructure-keys': ['warn', {
+ caseSensitive: false,
+ }],
'@typescript-eslint/explicit-function-return-type': 'off', // Allows functional components, should be fixed soon: https://github.com/typescript-eslint/typescript-eslint/issues/149
'@typescript-eslint/explicit-member-accessibility': 'off', // Allows not having to set public/private on class properties.
},
diff --git a/.gitignore b/.gitignore
index 61cf30ef2..aa5d4d0a0 100755
--- a/.gitignore
+++ b/.gitignore
@@ -3,20 +3,25 @@ logs
*.log
npm-debug.log*
lerna-debug.log*
+yarn-debug.log*
yarn-error.log*
*.swp
# Runtime data
pids
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
*.pid
*.seed
-# Coverage directory used by tools like istanbul
+# Testing
coverage
-# Dependency directories
-/node_modules
-/jspm_packages
+# Dependencies
+node_modules
+jspm_packages
# Optional npm cache directory
.npm
@@ -32,10 +37,13 @@ coverage
*.iml
.vscode/chrome
-# build folders
+# production folders
dist
build
# OS
.DS_Store
-__generated__
\ No newline at end of file
+__generated__
+
+#OncoJS development
+/src/packages/@oncojs
diff --git a/config-overrides.js b/config-overrides.js
index 032628e7d..7ba9956cc 100644
--- a/config-overrides.js
+++ b/config-overrides.js
@@ -1,21 +1,29 @@
-const { injectBabelPlugin } = require("react-app-rewired");
+const { injectBabelPlugin } = require('react-app-rewired');
module.exports = function override(config, env) {
config = injectBabelPlugin(
[
- "import-inspector",
+ 'import-inspector',
{
serverSideRequirePath: false,
- webpackRequireWeakId: true
- }
+ webpackRequireWeakId: true,
+ },
],
config
);
config = injectBabelPlugin(
- ["relay", { compat: true, schema: "data/schema.graphql" }],
+ [
+ 'relay',
+ {
+ compat: true,
+ schema: 'data/schema.graphql',
+ },
+ ],
config
);
+ env === 'development' && (config.devtool = 'eval-source-map');
+
return config;
};
diff --git a/package-lock.json b/package-lock.json
index 4c0b19ef6..2beae2cfa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,22 @@
"@babel/highlight": "^7.0.0"
}
},
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz",
+ "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
+ "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
"@babel/highlight": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
@@ -76,6 +92,41 @@
}
}
},
+ "@babel/types": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
+ "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ },
+ "dependencies": {
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+ }
+ }
+ },
+ "@emotion/is-prop-valid": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz",
+ "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==",
+ "requires": {
+ "@emotion/memoize": "0.7.1"
+ }
+ },
+ "@emotion/memoize": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz",
+ "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg=="
+ },
+ "@emotion/unitless": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz",
+ "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg=="
+ },
"@jephuff/canvg": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@jephuff/canvg/-/canvg-1.0.0.tgz",
@@ -85,6 +136,42 @@
"stackblur": "^1.0.0"
}
},
+ "@oncojs/boxplot": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@oncojs/boxplot/-/boxplot-0.2.0.tgz",
+ "integrity": "sha512-upYkFy0lqw95H1rbH8eLj2BXyk6ybH4A+IAmhWa+fJWx/BaRGDlbld1kAv1Lqm2RrZDfNo7pijY/i32muJFxKg==",
+ "requires": {
+ "d3-axis": "1.0.12",
+ "d3-scale": "3.0.0",
+ "d3-selection": "1.4.0",
+ "react-resize-detector": "4.1.3",
+ "styled-components": "4.2.0"
+ },
+ "dependencies": {
+ "d3-axis": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
+ "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
+ },
+ "d3-scale": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.0.0.tgz",
+ "integrity": "sha512-ktic5HBFlAZj2CN8CCl/p/JyY8bMQluN7+fA6ICE6yyoMOnSQAZ1Bb8/5LcNpNKMBMJge+5Vv4pWJhARYlQYFw==",
+ "requires": {
+ "d3-array": "^1.2.0 || 2",
+ "d3-format": "1",
+ "d3-interpolate": "1",
+ "d3-time": "1",
+ "d3-time-format": "2"
+ }
+ },
+ "d3-selection": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz",
+ "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg=="
+ }
+ }
+ },
"@oncojs/react-lolliplot": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/@oncojs/react-lolliplot/-/react-lolliplot-0.11.1.tgz",
@@ -351,7 +438,7 @@
},
"@types/filesize": {
"version": "3.6.0",
- "resolved": "https://registry.npmjs.org/@types/filesize/-/filesize-3.6.0.tgz",
+ "resolved": "http://registry.npmjs.org/@types/filesize/-/filesize-3.6.0.tgz",
"integrity": "sha512-rOWxCKMjt2DBuwddUnl5GOpf/jAkkqteB+XldncpVxVX+HPTmK2c5ACMOVEbp9gaH81IlhTdC3TwvRa5nopasw=="
},
"@types/geojson": {
@@ -366,7 +453,7 @@
},
"@types/json5": {
"version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "resolved": "http://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
@@ -430,6 +517,14 @@
"@types/react-icon-base": "*"
}
},
+ "@types/react-outside-click-handler": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@types/react-outside-click-handler/-/react-outside-click-handler-1.2.0.tgz",
+ "integrity": "sha512-yejuPUAzmlMmFuPGPYY1nxKj9NI7nn8qaCsyK1hte+Qy488+1d49+tVXDOP/N4mHFN+UjNt8HXQpIyVpRDI/YQ==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/react-redux": {
"version": "6.0.9",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-6.0.9.tgz",
@@ -727,7 +822,7 @@
},
"ansi-regex": {
"version": "0.2.1",
- "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
"integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk="
},
"ansi-styles": {
@@ -1054,7 +1149,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
@@ -1079,7 +1174,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@@ -1338,7 +1433,7 @@
},
"babel-plugin-istanbul": {
"version": "4.1.6",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
+ "resolved": "http://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
"integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==",
"dev": true,
"requires": {
@@ -1365,6 +1460,17 @@
"graphql": "^0.10.5"
}
},
+ "babel-plugin-styled-components": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.0.tgz",
+ "integrity": "sha512-sQVKG8irFXx14ZfaK1bBePirfkacl3j8nZwSZK+ZjsbnadRHKQTbhXbe/RB1vT6Vgkz45E+V95LBq4KqdhZUNw==",
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-module-imports": "^7.0.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "lodash": "^4.17.10"
+ }
+ },
"babel-plugin-syntax-async-functions": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
@@ -1404,8 +1510,7 @@
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
- "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
- "dev": true
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
},
"babel-plugin-syntax-object-rest-spread": {
"version": "6.13.0",
@@ -2390,7 +2495,7 @@
},
"browserify-aes": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
@@ -2624,6 +2729,11 @@
}
}
},
+ "camelize": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+ "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+ },
"caniuse-api": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
@@ -2688,7 +2798,7 @@
},
"chalk": {
"version": "0.5.1",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
"integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
"requires": {
"ansi-styles": "^1.1.0",
@@ -2793,7 +2903,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -2815,7 +2925,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -2911,7 +3021,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -3017,7 +3127,7 @@
},
"commander": {
"version": "2.6.0",
- "resolved": "http://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
"integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0="
},
"commondir": {
@@ -3217,7 +3327,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -3263,7 +3373,7 @@
},
"create-hash": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
@@ -3276,7 +3386,7 @@
},
"create-hmac": {
"version": "1.1.7",
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
@@ -3334,6 +3444,11 @@
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
"dev": true
},
+ "css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
+ },
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@@ -3342,7 +3457,7 @@
},
"css-in-js-utils": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
+ "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
"integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==",
"requires": {
"hyphenate-style-name": "^1.0.2",
@@ -3385,7 +3500,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -3427,7 +3542,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -3472,6 +3587,16 @@
}
}
},
+ "css-to-react-native": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.1.tgz",
+ "integrity": "sha512-yO+oEx1Lf+hDKasqQRVrAvzMCz825Huh1VMlEEDlRWyAhFb/FWb6I0KpEF1PkyKQ7NEdcx9d5M2ZEWgJAsgPvQ==",
+ "requires": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^3.3.0"
+ }
+ },
"css-what": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz",
@@ -3562,7 +3687,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -3604,7 +3729,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -3927,9 +4052,9 @@
}
},
"damerau-levenshtein": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
- "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz",
+ "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==",
"dev": true
},
"dashdash": {
@@ -4152,7 +4277,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
- "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"requires": {
@@ -4200,6 +4325,14 @@
"esutils": "^2.0.2"
}
},
+ "document.contains": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/document.contains/-/document.contains-1.0.1.tgz",
+ "integrity": "sha512-A1KqlZq1w605bwiiLqVZehWE9S9UYlUXPoduFWi64pNVNQ9vy6wwH/7BS+iEfSlF1YyZgcg5PZw5HqDi7FCrUw==",
+ "requires": {
+ "define-properties": "^1.1.3"
+ }
+ },
"dom-converter": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
@@ -4721,9 +4854,9 @@
"dev": true
},
"globals": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz",
- "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==",
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true
},
"has-flag": {
@@ -4948,9 +5081,9 @@
}
},
"resolve": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
- "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
+ "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -4981,18 +5114,18 @@
}
},
"eslint-plugin-react": {
- "version": "7.12.4",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz",
- "integrity": "sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==",
+ "version": "7.13.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz",
+ "integrity": "sha512-uA5LrHylu8lW/eAH3bEQe9YdzpPaFd9yAJTwTi/i/BKTD7j6aQMKVAdGM/ML72zD6womuSK7EiGtMKuK06lWjQ==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
"doctrine": "^2.1.0",
"has": "^1.0.3",
- "jsx-ast-utils": "^2.0.1",
+ "jsx-ast-utils": "^2.1.0",
"object.fromentries": "^2.0.0",
- "prop-types": "^15.6.2",
- "resolve": "^1.9.0"
+ "prop-types": "^15.7.2",
+ "resolve": "^1.10.1"
},
"dependencies": {
"doctrine": {
@@ -5016,9 +5149,9 @@
}
},
"resolve": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
- "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
+ "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -5032,6 +5165,15 @@
"integrity": "sha512-lHBVRIaz5ibnIgNG07JNiAuBUeKhEf8l4etNx5vfAEwqQ5tcuK3jV9yjmopPgQDagQb7HwIuQVsE3IVcGrRnag==",
"dev": true
},
+ "eslint-plugin-sort-destructure-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-sort-destructure-keys/-/eslint-plugin-sort-destructure-keys-1.3.0.tgz",
+ "integrity": "sha512-tZZhhCnuKOxJPvlNWrdPzvdxHXy1ayK98XfYIK3vgKBkfXQg8PYICmnjEDYGouB5NH5kotI34xohdXpReZQ76w==",
+ "dev": true,
+ "requires": {
+ "natural-compare-lite": "^1.4.0"
+ }
+ },
"eslint-restricted-globals": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
@@ -5211,7 +5353,7 @@
},
"expect": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/expect/-/expect-22.4.3.tgz",
"integrity": "sha512-XcNXEPehqn8b/jm8FYotdX0YrXn36qp4HWlrVT4ktwQas1l1LPxiVWncYnnL2eyMtKAmVIaG0XAp0QlrqJaxaA==",
"dev": true,
"requires": {
@@ -5257,7 +5399,7 @@
},
"jest-diff": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz",
"integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==",
"dev": true,
"requires": {
@@ -5269,7 +5411,7 @@
},
"jest-matcher-utils": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz",
"integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==",
"dev": true,
"requires": {
@@ -5280,7 +5422,7 @@
},
"jest-message-util": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz",
"integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==",
"dev": true,
"requires": {
@@ -5293,13 +5435,13 @@
},
"jest-regex-util": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz",
"integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==",
"dev": true
},
"pretty-format": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
"integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==",
"dev": true,
"requires": {
@@ -5396,7 +5538,7 @@
},
"external-editor": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
"dev": true,
"requires": {
@@ -5892,7 +6034,7 @@
},
"finalhandler": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"dev": true,
"requires": {
@@ -6050,7 +6192,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -6072,7 +6214,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -6173,8 +6315,8 @@
"dev": true,
"optional": true,
"requires": {
- "nan": "2.11.1",
- "node-pre-gyp": "0.10.0"
+ "nan": "^2.9.2",
+ "node-pre-gyp": "^0.10.0"
},
"dependencies": {
"abbrev": {
@@ -6184,16 +6326,12 @@
"optional": true
},
"ansi-regex": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
+ "version": "2.1.1",
+ "bundled": true
},
"ansi-styles": {
"version": "2.2.1",
- "bundled": true,
- "dev": true,
- "optional": true
+ "bundled": true
},
"aproba": {
"version": "1.2.0",
@@ -6219,7 +6357,7 @@
"bundled": true,
"optional": true,
"requires": {
- "balanced-match": "1.0.0",
+ "balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
@@ -6228,26 +6366,16 @@
"bundled": true,
"dev": true,
"optional": 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",
- "bundled": true,
- "dev": true,
- "optional": true
+ "bundled": true
}
}
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"concat-map": {
"version": "0.0.1",
@@ -6262,8 +6390,7 @@
},
"core-util-is": {
"version": "1.0.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"debug": {
"version": "2.6.9",
@@ -6271,13 +6398,13 @@
"dev": true,
"optional": true,
"requires": {
- "ms": "0.7.1"
+ "ms": "2.0.0"
},
"dependencies": {
"ms": {
- "version": "0.7.1",
- "resolved": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
- "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true,
"optional": true
}
@@ -6305,7 +6432,7 @@
"dev": true,
"optional": true,
"requires": {
- "minipass": "2.2.4"
+ "minipass": "^2.2.1"
}
},
"fs.realpath": {
@@ -6319,14 +6446,14 @@
"dev": true,
"optional": true,
"requires": {
- "aproba": "1.2.0",
- "console-control-strings": "1.1.0",
- "has-unicode": "2.0.1",
- "object-assign": "4.1.1",
- "signal-exit": "3.0.2",
- "string-width": "1.0.2",
- "strip-ansi": "3.0.1",
- "wide-align": "1.1.2"
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
},
"dependencies": {
"object-assign": {
@@ -6364,8 +6491,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
- "dev": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -6382,7 +6507,7 @@
"dev": true,
"optional": true,
"requires": {
- "safer-buffer": "2.1.2"
+ "safer-buffer": "^2.1.0"
}
},
"ignore-walk": {
@@ -6391,7 +6516,7 @@
"dev": true,
"optional": true,
"requires": {
- "minimatch": "3.0.4"
+ "minimatch": "^3.0.4"
}
},
"inflight": {
@@ -6399,14 +6524,13 @@
"bundled": true,
"optional": true,
"requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
+ "once": "^1.3.0",
+ "wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"ini": {
"version": "1.3.5",
@@ -6417,22 +6541,20 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
- "optional": true,
"requires": {
- "number-is-nan": "1.0.1"
+ "number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
- "brace-expansion": "1.1.11"
+ "brace-expansion": "^1.1.7"
}
},
"minimist": {
@@ -6447,8 +6569,8 @@
"dev": true,
"optional": true,
"requires": {
- "safe-buffer": "5.1.1",
- "yallist": "3.0.2"
+ "safe-buffer": "^5.1.1",
+ "yallist": "^3.0.0"
}
},
"minizlib": {
@@ -6457,7 +6579,7 @@
"dev": true,
"optional": true,
"requires": {
- "minipass": "2.2.4"
+ "minipass": "^2.2.1"
}
},
"mkdirp": {
@@ -6479,9 +6601,9 @@
"dev": true,
"optional": true,
"requires": {
- "debug": "2.6.9",
- "iconv-lite": "0.4.21",
- "sax": "1.2.4"
+ "debug": "^2.1.2",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
}
},
"node-pre-gyp": {
@@ -6490,16 +6612,16 @@
"dev": true,
"optional": true,
"requires": {
- "detect-libc": "1.0.3",
- "mkdirp": "0.5.1",
- "needle": "2.2.0",
- "nopt": "4.0.1",
- "npm-packlist": "1.1.10",
- "npmlog": "4.1.2",
- "rc": "1.2.7",
- "rimraf": "2.6.2",
- "semver": "5.5.0",
- "tar": "4.4.1"
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.0",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.1.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
}
},
"nopt": {
@@ -6508,8 +6630,8 @@
"dev": true,
"optional": true,
"requires": {
- "abbrev": "1.1.1",
- "osenv": "0.1.5"
+ "abbrev": "1",
+ "osenv": "^0.1.4"
}
},
"npm-bundled": {
@@ -6524,8 +6646,8 @@
"dev": true,
"optional": true,
"requires": {
- "ignore-walk": "3.0.1",
- "npm-bundled": "1.0.3"
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
}
},
"npmlog": {
@@ -6534,10 +6656,10 @@
"dev": true,
"optional": true,
"requires": {
- "are-we-there-yet": "1.1.4",
- "console-control-strings": "1.1.0",
- "gauge": "2.7.4",
- "set-blocking": "2.0.0"
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
},
"dependencies": {
"are-we-there-yet": {
@@ -6555,8 +6677,7 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"object-assign": {
"version": "4.1.0",
@@ -6567,7 +6688,7 @@
"bundled": true,
"optional": true,
"requires": {
- "wrappy": "1.0.2"
+ "wrappy": "1"
}
},
"osenv": {
@@ -6576,8 +6697,8 @@
"dev": true,
"optional": true,
"requires": {
- "os-homedir": "1.0.2",
- "os-tmpdir": "1.0.2"
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
}
},
"path-is-absolute": {
@@ -6591,8 +6712,7 @@
},
"process-nextick-args": {
"version": "2.0.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"rc": {
"version": "1.2.7",
@@ -6600,10 +6720,10 @@
"dev": true,
"optional": true,
"requires": {
- "deep-extend": "0.5.1",
- "ini": "1.3.5",
- "minimist": "1.2.0",
- "strip-json-comments": "2.0.1"
+ "deep-extend": "^0.5.1",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
@@ -6625,13 +6745,13 @@
"version": "2.3.6",
"bundled": true,
"requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "2.0.0",
- "safe-buffer": "5.1.1",
- "string_decoder": "1.1.1",
- "util-deprecate": "1.0.2"
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
}
},
"rimraf": {
@@ -6640,7 +6760,7 @@
"dev": true,
"optional": true,
"requires": {
- "glob": "7.1.2"
+ "glob": "^7.0.5"
},
"dependencies": {
"glob": {
@@ -6698,24 +6818,23 @@
"version": "1.0.2",
"bundled": true,
"requires": {
- "code-point-at": "1.1.0",
- "is-fullwidth-code-point": "1.0.0",
- "strip-ansi": "3.0.1"
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
}
},
"string_decoder": {
"version": "1.1.1",
"bundled": true,
- "optional": true,
"requires": {
- "safe-buffer": "5.1.1"
+ "safe-buffer": "~5.1.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
},
"dependencies": {
"ansi-regex": {
@@ -6739,19 +6858,18 @@
"dev": true,
"optional": true,
"requires": {
- "chownr": "1.0.1",
- "fs-minipass": "1.2.5",
- "minipass": "2.2.4",
- "minizlib": "1.1.0",
- "mkdirp": "0.5.1",
- "safe-buffer": "5.1.1",
- "yallist": "3.0.2"
+ "chownr": "^1.0.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.2.4",
+ "minizlib": "^1.1.0",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.1",
+ "yallist": "^3.0.2"
}
},
"util-deprecate": {
"version": "1.0.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"wide-align": {
"version": "1.1.2",
@@ -6802,7 +6920,7 @@
},
"get-stream": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
},
@@ -6941,7 +7059,7 @@
},
"got": {
"version": "6.7.1",
- "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+ "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"dev": true,
"requires": {
@@ -7311,7 +7429,7 @@
},
"http-errors": {
"version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"dev": true,
"requires": {
@@ -8190,7 +8308,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8250,7 +8368,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8303,7 +8421,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8325,7 +8443,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8366,7 +8484,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8388,7 +8506,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8527,7 +8645,7 @@
},
"jest-get-type": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
"integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
"dev": true
},
@@ -8576,7 +8694,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8598,7 +8716,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8637,7 +8755,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8659,7 +8777,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8711,7 +8829,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8733,7 +8851,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8817,7 +8935,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8839,7 +8957,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8888,7 +9006,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8910,7 +9028,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -8954,7 +9072,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -8976,7 +9094,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -9017,7 +9135,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -9039,7 +9157,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -9630,6 +9748,11 @@
"mimic-fn": "^1.0.0"
}
},
+ "memoize-one": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz",
+ "integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA=="
+ },
"memoizee": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.5.tgz",
@@ -9675,7 +9798,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -9777,7 +9900,7 @@
},
"minimist": {
"version": "0.0.8",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mississippi": {
@@ -9924,6 +10047,12 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
+ "natural-compare-lite": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+ "integrity": "sha1-F7CVgZiJef3a/gIB6TG6kzyWy7Q=",
+ "dev": true
+ },
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@@ -10745,7 +10874,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -10787,7 +10916,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -10830,7 +10959,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -10872,7 +11001,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -10905,7 +11034,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -10947,7 +11076,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -10979,7 +11108,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11021,7 +11150,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11053,7 +11182,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11095,7 +11224,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11127,7 +11256,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11169,7 +11298,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11201,7 +11330,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11243,7 +11372,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11276,7 +11405,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11318,7 +11447,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11350,7 +11479,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11392,7 +11521,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11479,7 +11608,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11521,7 +11650,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11553,7 +11682,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11595,7 +11724,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11641,7 +11770,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11683,7 +11812,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11723,7 +11852,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11765,7 +11894,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11798,7 +11927,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11840,7 +11969,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11875,7 +12004,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -11926,7 +12055,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -11961,7 +12090,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12003,7 +12132,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12074,7 +12203,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12116,7 +12245,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12151,7 +12280,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12193,7 +12322,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12226,7 +12355,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12268,7 +12397,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12301,7 +12430,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12343,7 +12472,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12375,7 +12504,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12417,7 +12546,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12451,7 +12580,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12493,7 +12622,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12539,7 +12668,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12581,7 +12710,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12615,7 +12744,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12657,7 +12786,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12669,8 +12798,7 @@
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
},
"postcss-zindex": {
"version": "2.2.0",
@@ -12697,7 +12825,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -12739,7 +12867,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12991,6 +13119,11 @@
"performance-now": "^2.1.0"
}
},
+ "raf-schd": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.0.tgz",
+ "integrity": "sha512-m7zq0JkIrECzw9mO5Zcq6jN4KayE34yoIS9hJoiZNXyOAT06PPA8PrR+WtJIeFW09YjUfNkMMN9lrmAt6BURCA=="
+ },
"randomatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz",
@@ -13117,7 +13250,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -13235,7 +13368,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -13263,7 +13396,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -13355,8 +13488,7 @@
"react-is": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
- "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==",
- "dev": true
+ "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
@@ -13413,6 +13545,69 @@
"moment": ">=1.6.0"
}
},
+ "react-outside-click-handler": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/react-outside-click-handler/-/react-outside-click-handler-1.2.3.tgz",
+ "integrity": "sha512-4orkx59ais0mM/j1Ekc5ewyRu5xNLX4a6pMs7RT8U7JkbPOlRsucE+190kXzYUUHsGfZvyAmsdQkL7lpqzMGBg==",
+ "requires": {
+ "airbnb-prop-types": "^2.12.0",
+ "consolidated-events": "^1.1.1 || ^2.0.0",
+ "document.contains": "^1.0.0",
+ "object.values": "^1.1.0",
+ "prop-types": "^15.7.2"
+ },
+ "dependencies": {
+ "airbnb-prop-types": {
+ "version": "2.13.2",
+ "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz",
+ "integrity": "sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ==",
+ "requires": {
+ "array.prototype.find": "^2.0.4",
+ "function.prototype.name": "^1.1.0",
+ "has": "^1.0.3",
+ "is-regex": "^1.0.4",
+ "object-is": "^1.0.1",
+ "object.assign": "^4.1.0",
+ "object.entries": "^1.1.0",
+ "prop-types": "^15.7.2",
+ "prop-types-exact": "^1.2.0",
+ "react-is": "^16.8.6"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
+ "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ }
+ }
+ },
"react-portal": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-portal/-/react-portal-3.2.0.tgz",
@@ -13464,6 +13659,30 @@
"resolved": "https://registry.npmjs.org/react-relay-network-layer/-/react-relay-network-layer-2.0.1.tgz",
"integrity": "sha1-bbkbGZ90cz6YDQd27G+nlh4BVNA="
},
+ "react-resize-detector": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-4.1.3.tgz",
+ "integrity": "sha512-tzVDVFxhUVdTjxyR1OqNggkbFAA7srawgwhcm4XQjyGq/R8M13MiceI+4C7xfzHqaKk0Oq7ErIXexGGSgQEKcQ==",
+ "requires": {
+ "lodash": "^4.17.11",
+ "lodash-es": "^4.17.11",
+ "prop-types": "^15.7.2",
+ "raf-schd": "^4.0.0",
+ "resize-observer-polyfill": "^1.5.1"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ }
+ }
+ },
"react-router": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
@@ -13570,7 +13789,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -13607,7 +13826,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -13842,7 +14061,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -14472,7 +14691,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@@ -14494,7 +14713,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -14573,7 +14792,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -14680,6 +14899,11 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
+ "resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+ },
"resolve": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz",
@@ -14816,9 +15040,9 @@
}
},
"rxjs": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.1.tgz",
- "integrity": "sha512-y0j31WJc83wPu31vS1VlAFW5JGrnGC+j+TtGAa1fRQphy48+fDYiDmX8tjGloToEsMkxnouOg/1IzXGKkJnZMg==",
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
+ "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
@@ -14877,7 +15101,7 @@
},
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -15064,7 +15288,7 @@
},
"sha.js": {
"version": "2.4.11",
- "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
@@ -15122,6 +15346,11 @@
"integrity": "sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo=",
"dev": true
},
+ "simple-statistics": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.0.2.tgz",
+ "integrity": "sha512-wqTjlmbiaL6Fqaw28tSjQrthjxVV17MMfi/H/qvE0jAvtLspB2S7gEtcR27uvhSRAa64LhjhoO169rX8sFW3pg=="
+ },
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -15566,7 +15795,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -15614,7 +15843,7 @@
},
"strip-ansi": {
"version": "0.3.0",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
"integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
"requires": {
"ansi-regex": "^0.2.1"
@@ -15673,6 +15902,49 @@
"schema-utils": "^0.3.0"
}
},
+ "styled-components": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.2.0.tgz",
+ "integrity": "sha512-L/LzkL3ZbBhqIVHdR7DbYujy4tqvTNRfc+4JWDCYyhTatI+8CRRQUmdaR0+ARl03DWsfKLhjewll5uNLrqrl4A==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@emotion/is-prop-valid": "^0.7.3",
+ "@emotion/unitless": "^0.7.0",
+ "babel-plugin-styled-components": ">= 1",
+ "css-to-react-native": "^2.2.2",
+ "memoize-one": "^5.0.0",
+ "prop-types": "^15.5.4",
+ "react-is": "^16.6.0",
+ "stylis": "^3.5.0",
+ "stylis-rule-sheet": "^0.0.10",
+ "supports-color": "^5.5.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "stylis": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
+ "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+ },
+ "stylis-rule-sheet": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
+ "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
+ },
"subarg": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
@@ -15684,7 +15956,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -15763,9 +16035,9 @@
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY="
},
"table": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
- "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.3.3.tgz",
+ "integrity": "sha512-3wUNCgdWX6PNpOe3amTTPWPuF6VGvgzjKCaO1snFj0z7Y3mUPWf5+zDtxUVGispJkDECPmR29wbzh6bVMOHbcw==",
"dev": true,
"requires": {
"ajv": "^6.9.1",
@@ -16177,7 +16449,7 @@
},
"jest-diff": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz",
"integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==",
"dev": true,
"requires": {
@@ -16189,7 +16461,7 @@
},
"jest-environment-jsdom": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz",
"integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==",
"dev": true,
"requires": {
@@ -16200,7 +16472,7 @@
},
"jest-environment-node": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz",
"integrity": "sha512-reZl8XF6t/lMEuPWwo9OLfttyC26A5AMgDyEQ6DBgZuyfyeNUzYT8BFo6uxCCP/Av/b7eb9fTi3sIHFPBzmlRA==",
"dev": true,
"requires": {
@@ -16229,7 +16501,7 @@
},
"jest-matcher-utils": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz",
"integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==",
"dev": true,
"requires": {
@@ -16240,7 +16512,7 @@
},
"jest-message-util": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz",
"integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==",
"dev": true,
"requires": {
@@ -16253,19 +16525,19 @@
},
"jest-mock": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz",
+ "resolved": "http://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",
+ "resolved": "http://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",
+ "resolved": "http://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz",
"integrity": "sha512-u3BkD/MQBmwrOJDzDIaxpyqTxYH+XqAXzVJP51gt29H8jpj3QgKof5GGO2uPGKGeA1yTMlpbMs1gIQ6U4vcRhw==",
"dev": true,
"requires": {
@@ -16275,7 +16547,7 @@
},
"jest-snapshot": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz",
"integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==",
"dev": true,
"requires": {
@@ -16289,7 +16561,7 @@
},
"jest-util": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz",
"integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==",
"dev": true,
"requires": {
@@ -16337,7 +16609,7 @@
},
"pretty-format": {
"version": "22.4.3",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
+ "resolved": "http://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
"integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==",
"dev": true,
"requires": {
@@ -16791,7 +17063,7 @@
},
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
@@ -17994,7 +18266,7 @@
},
"load-json-file": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
@@ -18080,7 +18352,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -18575,7 +18847,7 @@
"dependencies": {
"pify": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
}
@@ -18686,7 +18958,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -18939,7 +19211,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
diff --git a/package.json b/package.json
index b881fe40f..550822e5e 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "version": "1.21.0",
+ "version": "1.22.0",
"repository": "NCI-GDC/portal-ui",
"name": "portal-ui",
"author": {
@@ -25,6 +25,7 @@
}
},
"dependencies": {
+ "@oncojs/boxplot": "0.2.0",
"@oncojs/react-lolliplot": "0.11.1",
"@oncojs/sapien": "0.5.2",
"@oncojs/survivalplot": "0.8.2",
@@ -37,6 +38,7 @@
"@types/react": "16.4.18",
"@types/react-dom": "16.0.9",
"@types/react-icons": "2.2.6",
+ "@types/react-outside-click-handler": "1.2.0",
"@types/react-redux": "6.0.9",
"@types/react-relay": "1.3.9",
"@types/react-router-dom": "4.3.1",
@@ -80,6 +82,7 @@
"react-loadable": "4.0.2",
"react-markdown": "3.0.1",
"react-modal": "3.0.0",
+ "react-outside-click-handler": "1.2.3",
"react-progress": "0.0.12",
"react-redux": "5.0.4",
"react-relay": "1.3.0",
@@ -93,18 +96,20 @@
"redux-api-middleware": "2.0.0-beta.2",
"redux-persist": "5.9.1",
"redux-thunk": "2.2.0",
+ "simple-statistics": "7.0.2",
"url-join": "1.1.0"
},
"devDependencies": {
"babel-plugin-import-inspector": "2.0.0",
"babel-plugin-relay": "1.3.0",
"babel-plugin-transform-export-extensions": "6.22.0",
- "eslint": "5.16.0",
- "eslint-config-airbnb": "17.1.0",
- "eslint-plugin-import": "2.17.2",
- "eslint-plugin-jsx-a11y": "6.2.1",
- "eslint-plugin-react": "7.12.4",
- "eslint-plugin-react-hooks": "1.6.0",
+ "eslint": "^5.16.0",
+ "eslint-config-airbnb": "^17.1.0",
+ "eslint-plugin-import": "^2.17.2",
+ "eslint-plugin-jsx-a11y": "^6.2.1",
+ "eslint-plugin-react": "^7.13.0",
+ "eslint-plugin-react-hooks": "^1.6.0",
+ "eslint-plugin-sort-destructure-keys": "^1.3.0",
"flow-bin": "0.61.0",
"react-app-rewired": "1.6.2",
"react-scripts-ts": "3.1.0",
diff --git a/public/gdc-templates.js b/public/gdc-templates.js
index 3ae7b3af1..7ae58a535 100644
--- a/public/gdc-templates.js
+++ b/public/gdc-templates.js
@@ -369,7 +369,7 @@ try {
}
module.run(['$templateCache', function($templateCache) {
$templateCache.put('home/templates/home.html',
- '
Harmonized Cancer Datasets Genomic Data Commons Data Portal Get Started by Exploring: Perform Advanced Search Queries, such as: Infrastructure Data is continuously being processed and harmonized by the GDC. View GDC system statistics:
Documentation Learn how to use the GDC Data Portal to its full potential with common topics such as:
GDC Applications The GDC Data Portal is a robust data-driven platform that allows cancer researchers and bioinformaticians to search and download cancer data for analysis. The GDC applications include: ');
+ 'Harmonized Cancer Datasets Genomic Data Commons Data Portal Get Started by Exploring: Perform Advanced Search Queries, such as: Infrastructure Data is continuously being processed and harmonized by the GDC. View GDC system statistics:
Documentation Learn how to use the GDC Data Portal to its full potential with common topics such as:
GDC Applications The GDC Data Portal is a robust data-driven platform that allows cancer researchers and bioinformaticians to search and download cancer data for analysis. The GDC applications include: ');
}]);
})();
@@ -838,7 +838,7 @@ try {
}
module.run(['$templateCache', function($templateCache) {
$templateCache.put('components/header/templates/header.html',
- '');
+ '');
}]);
})();
diff --git a/pull_request_template.md b/pull_request_template.md
new file mode 100644
index 000000000..715381391
--- /dev/null
+++ b/pull_request_template.md
@@ -0,0 +1,11 @@
+## Ticket Number
+
+e.g. PRTL-0000
+
+## Environment to be used in testing
+
+- [ ] `prod`
+- [ ] `dev-oicr`
+- [ ] `qa` (which version?)
+
+## Description of Changes
diff --git a/src/packages/@ncigdc/components/Aggregations/FacetHeader.js b/src/packages/@ncigdc/components/Aggregations/FacetHeader.js
index 597be6885..51d289514 100644
--- a/src/packages/@ncigdc/components/Aggregations/FacetHeader.js
+++ b/src/packages/@ncigdc/components/Aggregations/FacetHeader.js
@@ -1,7 +1,11 @@
/* @flow */
import React from 'react';
-import { compose, defaultProps } from 'recompose';
+import {
+ compose,
+ defaultProps,
+ setDisplayName,
+} from 'recompose';
import { css } from 'glamor';
import { parseFilterParam } from '@ncigdc/utils/uri';
@@ -53,6 +57,7 @@ const MagnifyingGlass = styled(SearchIcon, {
});
const FacetHeader = compose(
+ setDisplayName('EnhancedFacetHeader'),
defaultProps({
handleRequestRemove: () => {},
isRemovable: false,
@@ -61,16 +66,16 @@ const FacetHeader = compose(
}),
)(
({
- field,
- title,
+ collapsed,
description,
- isRemovable,
+ field,
handleRequestRemove,
- collapsed,
+ hasValueSearch,
+ isRemovable,
setCollapsed,
- showingValueSearch,
setShowingValueSearch,
- hasValueSearch,
+ showingValueSearch,
+ title,
}) => (
{(ctx: { pathname: string, query: IRawQuery }) => {
@@ -79,15 +84,15 @@ const FacetHeader = compose(
return (
setCollapsed(!collapsed)}
- >
+ style={{ cursor: 'pointer' }}
+ >
+ />
{title}
@@ -95,25 +100,24 @@ const FacetHeader = compose(
+ >
)}
{hasValueSearch && (
setShowingValueSearch(!showingValueSearch)}
- />
+ />
)}
-
+
{isRemovable && (
- event.key === 'Enter' && handleRequestRemove()}
+ onKeyPress={event => event.key === 'Enter' && handleRequestRemove()}
role="button"
tabIndex="0"
- aria-label="Close"
- />
+ />
)}
diff --git a/src/packages/@ncigdc/components/Aggregations/SuggestionFacet.js b/src/packages/@ncigdc/components/Aggregations/SuggestionFacet.js
index d41c52796..0a0087037 100644
--- a/src/packages/@ncigdc/components/Aggregations/SuggestionFacet.js
+++ b/src/packages/@ncigdc/components/Aggregations/SuggestionFacet.js
@@ -3,7 +3,14 @@
// Vendor
import React from 'react';
import _ from 'lodash';
-import { compose, pure, withState, withHandlers, renameProp } from 'recompose';
+import {
+ compose,
+ pure,
+ renameProp,
+ setDisplayName,
+ withHandlers,
+ withState,
+} from 'recompose';
import SearchIcon from 'react-icons/lib/fa/search';
import LocationSubscriber from '@ncigdc/components/LocationSubscriber';
@@ -24,7 +31,7 @@ import Input from '@ncigdc/uikit/Form/Input';
import SetId from '@ncigdc/components/SetId';
import Hidden from '@ncigdc/components/Hidden';
-import { Container, CheckedRow, CheckedLink } from './';
+import { Container, CheckedRow, CheckedLink } from '.';
const MagnifyingGlass = styled(SearchIcon, {
backgroundColor: ({ theme }) => theme.greyScale5,
@@ -49,29 +56,26 @@ const StyledDropdownLink = styled(Link, {
color: ({ theme }) => theme.greyScale2,
':link': {
textDecoration: 'none',
- color: ({ theme, linkIsActive }) =>
- linkIsActive ? 'white' : theme.primary,
+ color: ({ linkIsActive, theme }) => (linkIsActive ? 'white' : theme.primary),
},
':visited': {
textDecoration: 'none',
- color: ({ theme, linkIsActive }) =>
- linkIsActive ? 'white' : theme.primary,
+ color: ({ linkIsActive, theme }) => (linkIsActive ? 'white' : theme.primary),
},
- backgroundColor: ({ linkIsActive }) =>
- linkIsActive ? 'rgb(31, 72, 108)' : 'inherit',
+ backgroundColor: ({ linkIsActive }) => (linkIsActive ? 'rgb(31, 72, 108)' : 'inherit'),
width: '100%',
textDecoration: 'none',
});
const SuggestionFacet = compose(
+ setDisplayName('EnhancedSuggestionFacet'),
withDropdown,
withState('inputValue', 'setInputValue', ''),
withState('isLoading', 'setIsLoading', false),
renameProp('hits', 'results'),
withHandlers({
// TODO: use router push
- handleSelectItem: () => item =>
- document.querySelector(`[data-link-id="${item.id}"]`).click(),
+ handleSelectItem: () => item => document.querySelector(`[data-link-id="${item.id}"]`).click(),
}),
namespace(
'selectableList',
@@ -88,24 +92,24 @@ const SuggestionFacet = compose(
pure,
)(
({
- isLoading,
- setIsLoading,
- selectableList,
- dropdownItem,
- title,
+ active,
+ collapsed,
doctype,
+ dropdownItem,
fieldNoDoctype,
+ inputValue,
+ isLoading,
+ mouseDownHandler,
+ mouseUpHandler,
placeholder,
results,
- setAutocomplete,
+ selectableList,
setActive,
- active,
- mouseDownHandler,
- mouseUpHandler,
- collapsed,
- style,
- inputValue,
+ setAutocomplete,
setInputValue,
+ setIsLoading,
+ style,
+ title,
}) => {
const query = v => ({
offset: 0,
@@ -144,7 +148,7 @@ const SuggestionFacet = compose(
dotField: `${doctype}.${fieldNoDoctype}`,
}) || { content: { value: [] } };
return (
-
+
{!collapsed && (
{[].concat(currentValues.content.value || []).map(v => (
@@ -166,7 +170,7 @@ const SuggestionFacet = compose(
],
},
}}
- >
+ >
{getCheckedValue(v)}
@@ -178,44 +182,44 @@ const SuggestionFacet = compose(
{title}
{
- const value = e.target.value;
+ const { value } = e.target;
setInputValue(value);
setActive(!!value);
- if (!!value) {
+ if (value) {
setIsLoading(true);
setAutocomplete(
value,
- readyState =>
- _.some([
- readyState.ready,
- readyState.aborted,
- readyState.error,
- ]) && setIsLoading(false),
+ readyState => _.some([
+ readyState.ready,
+ readyState.aborted,
+ readyState.error,
+ ]) && setIsLoading(false),
);
}
}}
onKeyDown={selectableList.handleKeyEvent}
placeholder={placeholder}
+ style={{ borderRadius: '0 4px 4px 0' }}
value={inputValue}
- aria-activedescendant={
- active
- ? _.get(
- selectableList,
- `focusedItem.${fieldNoDoctype}`,
- )
- : null // false gets stringify, so value needs to be `null` or `undefined`
- }
{...active && {
'aria-owns': `${fieldNoDoctype}-options`,
}}
- />
+ />
{active && (
e.stopPropagation()}
style={{
...dropdown,
marginTop: 0,
@@ -223,25 +227,24 @@ const SuggestionFacet = compose(
width: '300px',
wordBreak: 'break-word',
}}
- onClick={e => e.stopPropagation()}
- >
+ >
{(results || []).map(x => (
{
setInputValue('');
setActive(false);
}}
onMouseOver={() => selectableList.setFocusedItem(x)}
- >
+ style={{ alignItems: 'center' }}
+ >
+ merge="add"
+ query={query(x[fieldNoDoctype])}
+ >
{dropdownItem(x, inputValue)}
diff --git a/src/packages/@ncigdc/components/Charts/BarChart.js b/src/packages/@ncigdc/components/Charts/BarChart.js
index e2290e562..ec292cdb1 100644
--- a/src/packages/@ncigdc/components/Charts/BarChart.js
+++ b/src/packages/@ncigdc/components/Charts/BarChart.js
@@ -16,7 +16,7 @@ import { withTooltip } from '@ncigdc/uikit/Tooltip';
import withSize from '@ncigdc/utils/withSize';
import './style.css';
-export const DEFAULT_X_AXIS_LABEL_LENGTH = 8;
+export const DEFAULT_X_AXIS_LABEL_LENGTH = 10;
const BarChart = ({
data,
diff --git a/src/packages/@ncigdc/components/DownloadVisualizationButton.js b/src/packages/@ncigdc/components/DownloadVisualizationButton.js
index aab8d72c1..90ed9ba33 100644
--- a/src/packages/@ncigdc/components/DownloadVisualizationButton.js
+++ b/src/packages/@ncigdc/components/DownloadVisualizationButton.js
@@ -65,8 +65,10 @@ const DownloadVisualizationButton = ({
theme,
tooltipHTML,
disabled,
+ buttonStyle,
...props
-}: TProps) => (
+}: TProps) => {
+return (
}
- style={visualizingButton}
+ style={{...visualizingButton, ...buttonStyle}}
type="button"
>
{noText ? (
@@ -178,6 +180,6 @@ const DownloadVisualizationButton = ({
)}
-);
+)};
export default enhance(DownloadVisualizationButton);
diff --git a/src/packages/@ncigdc/components/EntityPageHorizontalTable.js b/src/packages/@ncigdc/components/EntityPageHorizontalTable.js
index 56f72fa67..32c8fdcd9 100644
--- a/src/packages/@ncigdc/components/EntityPageHorizontalTable.js
+++ b/src/packages/@ncigdc/components/EntityPageHorizontalTable.js
@@ -30,7 +30,7 @@ const EntityPageHorizontalTable = ({
rightComponent,
headings,
data,
- emptyMessage,
+ emptyMessage = 'No valid data to be shown',
emptyMessageStyle,
theme,
tableId,
@@ -38,117 +38,128 @@ const EntityPageHorizontalTable = ({
dividerStyle,
tableContainerStyle = {},
...props
-}) => (
-
- {(title || rightComponent) && (
- {
+ return (
+
- {title || } {rightComponent}
-
- )}
- {!!data.length && (
- (
- 0 ? dividerStyle : {}),
- }}
+ {(title || rightComponent) && (
+
- {h.title}
-
- ))}
- subheadings={headings.map(
- (h, i) =>
- h.subheadings &&
- h.subheadings.map((s, j) => (
+ {title || }
+ {' '}
+ {rightComponent}
+
+ )}
+ {data.length > 0
+ ? (
+
+ {data.map((d, k) => (
+
+ {headings.map((h, i) => [].concat(get(d, h.key, '--')).map((v, j) => (
+ 0 && j === 0 ? dividerStyle : {}),
+ }}
+ >
+ {h.color && (
+
+ )}
+
+ {v}
+
+ )))}
+
+ ))}
+
+ )}
+ headings={headings.map((h, i) => (
0 && j === 0 ? dividerStyle : {}),
+ ...(h.thStyle || h.style || {}),
+ ...(i > 0 ? dividerStyle : {}),
}}
- >
- {s}
+ >
+ {h.title}
- ))
- )}
- body={
-
- {data.map((d, k) => (
-
- {headings.map((h, i) =>
- [].concat(get(d, h.key, '--')).map((v, j) => (
- 0 && j === 0 ? dividerStyle : {}),
- }}
- className={h.className || ''}
- >
- {h.color && (
-
- )}
- {v}
-
- ))
- )}
-
))}
-
- }
- />
- )}
- {!data.length && (
-
- {emptyMessage && (
- {emptyMessage}
+ id={tableId}
+ style={styles.table}
+ subheadings={headings.map(
+ (h, i) => h.subheadings &&
+ h.subheadings.map((s, j) => (
+ 0 && j === 0 ? dividerStyle : {}),
+ }}
+ >
+ {s}
+
+ ))
+ )}
+ />
+ )
+ : (
+
+ {emptyMessage && (
+
+ {emptyMessage}
+
+ )}
+
)}
-
- )}
-
-);
+
+ );
+};
export default withTheme(EntityPageHorizontalTable);
diff --git a/src/packages/@ncigdc/components/FacetSelection.js b/src/packages/@ncigdc/components/FacetSelection.js
index 4a1af17ae..8745a5611 100644
--- a/src/packages/@ncigdc/components/FacetSelection.js
+++ b/src/packages/@ncigdc/components/FacetSelection.js
@@ -5,12 +5,13 @@ import _ from 'lodash';
import { css } from 'glamor';
import {
compose,
- withState,
+ defaultProps,
lifecycle,
- withProps,
renameProps,
- defaultProps,
+ setDisplayName,
withHandlers,
+ withProps,
+ withState,
} from 'recompose';
import { fetchApi } from '@ncigdc/utils/ajax';
import entityShortnameMapping from '@ncigdc/utils/entityShortnameMapping';
@@ -18,10 +19,10 @@ import Highlight from '@ncigdc/uikit/Highlight';
import withSelectableList from '@ncigdc/utils/withSelectableList';
import withPropsOnChange from '@ncigdc/utils/withPropsOnChange';
-const facetMatchesQuery = (facet, query) =>
- _.some([facet.field, facet.description].map(_.toLower), searchTarget =>
- _.includes(searchTarget, query),
- );
+const facetMatchesQuery = (facet, query) => _.some(
+ [facet.field, facet.description].map(_.toLower),
+ searchTarget => _.includes(searchTarget, query),
+);
const styles = {
header: {
@@ -107,14 +108,14 @@ const styles = {
};
// Highlighting is frustratingly slow with > 100 items
-const ConditionalHighlight = ({ condition, search, children }) =>
- condition ? (
- {children}
+const ConditionalHighlight = ({ children, condition, search }) => (condition ? (
+ {children}
) : (
{children}
- );
+ ));
export default compose(
+ setDisplayName('EnhancedFacetSelection'),
withState('facetMapping', 'setFacetMapping', {}),
withState('query', 'setQuery', ''),
withState('focusedFacet', 'setFocusedFacet', null),
@@ -126,19 +127,19 @@ export default compose(
),
withPropsOnChange(
['isLoadingFacetMapping', 'isLoadingAdditionalFacetData'],
- ({ isLoadingFacetMapping, isLoadingAdditionalFacetData }) => ({
+ ({ isLoadingAdditionalFacetData, isLoadingFacetMapping }) => ({
isLoading: _.some([isLoadingFacetMapping, isLoadingAdditionalFacetData]),
}),
),
withState('shouldHideUselessFacets', 'setShouldHideUselessFacets', false),
withProps(
({
+ docType,
+ facetMapping,
relay,
+ relayVarName,
setIsLoadingAdditionalFacetData,
setShouldHideUselessFacets,
- facetMapping,
- relayVarName,
- docType,
}) => ({
setUselessFacetVisibility: shouldHideUselessFacets => {
setShouldHideUselessFacets(shouldHideUselessFacets);
@@ -157,7 +158,11 @@ export default compose(
},
readyState => {
if (
- _.some([readyState.ready, readyState.aborted, readyState.error])
+ _.some([
+ readyState.ready,
+ readyState.aborted,
+ readyState.error,
+ ])
) {
setIsLoadingAdditionalFacetData(false);
}
@@ -169,8 +174,7 @@ export default compose(
),
withPropsOnChange(
['isLoadingFacetMapping'],
- ({ isLoadingFacetMapping, setUselessFacetVisibility }) =>
- !isLoadingFacetMapping &&
+ ({ isLoadingFacetMapping, setUselessFacetVisibility }) => !isLoadingFacetMapping &&
JSON.parse(localStorage.getItem('shouldHideUselessFacets') || 'null') &&
setUselessFacetVisibility(true),
),
@@ -183,8 +187,7 @@ export default compose(
setFacetMapping(mapping);
setIsLoadingFacetMapping(false);
},
- handleQueryInputChange: ({ setQuery }) => event =>
- setQuery(event.target.value),
+ handleQueryInputChange: ({ setQuery }) => event => setQuery(event.target.value),
}),
defaultProps({
excludeFacetsBy: _.noop,
@@ -194,8 +197,7 @@ export default compose(
withPropsOnChange(['additionalFacetData'], ({ additionalFacetData }) => ({
usefulFacets: _.omitBy(
additionalFacetData,
- aggregation =>
- !aggregation ||
+ aggregation => !aggregation ||
_.some([
aggregation.buckets &&
aggregation.buckets.filter(bucket => bucket.key !== '_missing')
@@ -208,20 +210,18 @@ export default compose(
})),
withProps(
({
- facetMapping,
excludeFacetsBy,
+ facetMapping,
query,
shouldHideUselessFacets,
usefulFacets,
}) => ({
- filteredFacets: _.filter(_.values(facetMapping), facet =>
- _.every([
- facetMatchesQuery(facet, query),
- !excludeFacetsBy(facet),
- !shouldHideUselessFacets ||
+ filteredFacets: _.filter(_.values(facetMapping), facet => _.every([
+ facetMatchesQuery(facet, query),
+ !excludeFacetsBy(facet),
+ !shouldHideUselessFacets ||
Object.keys(usefulFacets).includes(facet.field),
- ]),
- ),
+ ]),),
}),
),
renameProps({
@@ -229,10 +229,10 @@ export default compose(
}),
withHandlers({
handleClose: ({
- setQuery,
- setFocusedFacet,
onRequestClose,
relay,
+ setFocusedFacet,
+ setQuery,
}) => () => {
setQuery('');
setFocusedFacet(null);
@@ -265,29 +265,29 @@ export default compose(
margin: 0,
lineHeight: 1.42857143,
}}
- >
+ >
{props.title}
+ >
Cancel
Search for a field:
+ placeholder="search"
+ type="text"
+ />
{props.isLoading
@@ -295,15 +295,14 @@ export default compose(
: `${props.filteredFacets.length} ${props.docType} fields`}
-
+
- props.setUselessFacetVisibility(event.target.checked)}
checked={props.shouldHideUselessFacets}
+ className="test-filter-useful-facet"
+ onChange={event => props.setUselessFacetVisibility(event.target.checked)}
style={styles.uselessFacetVisibilityCheckbox}
- />
+ type="checkbox"
+ />
Only show fields with values
@@ -319,12 +318,15 @@ export default compose(
onClick={() => props.handleSelectFacet(facet)}
onMouseEnter={() => props.setFocusedFacet(facet)}
{...css(styles.facetItem)}
- >
+ >
{
entityShortnameMapping[
- { cases: 'case', files: 'file' }[facet.doc_type]
+ {
+ cases: 'case',
+ files: 'file',
+ }[facet.doc_type]
]
}
@@ -334,17 +336,17 @@ export default compose(
styles.facetTexts,
isFocused && styles.focusedItem.container,
)}
- >
+ >
+ >
= 2}
search={props.query}
- >
+ >
{facet.field}
{facet.type}
@@ -355,11 +357,11 @@ export default compose(
styles.facetDescription,
isFocused && styles.focusedItem.text,
)}
- >
+ >
= 2}
search={props.query}
- >
+ >
{facet.description}
diff --git a/src/packages/@ncigdc/components/FacetWrapper.js b/src/packages/@ncigdc/components/FacetWrapper.js
index 90a1af5cc..cf54aba62 100644
--- a/src/packages/@ncigdc/components/FacetWrapper.js
+++ b/src/packages/@ncigdc/components/FacetWrapper.js
@@ -3,7 +3,13 @@
import React from 'react';
import _ from 'lodash';
-import { compose, defaultProps, renameProps, withState } from 'recompose';
+import {
+ compose,
+ defaultProps,
+ renameProps,
+ setDisplayName,
+ withState,
+} from 'recompose';
import TermAggregation from '@ncigdc/components/Aggregations/TermAggregation';
import DateFacet from '@ncigdc/components/Aggregations/DateFacet';
@@ -33,31 +39,33 @@ const COMMON_PREPOSITIONS = [
'yet',
];
-const fieldNameToTitle = fieldName =>
- fieldName
- .replace(/_|\./g, ' ')
- .split(' ')
- .map(
- word => (COMMON_PREPOSITIONS.includes(word) ? word : _.capitalize(word)),
- )
- .join(' ');
+const fieldNameToTitle = fieldName => fieldName
+ .replace(/_|\./g, ' ')
+ .split(' ')
+ .map(
+ word => (COMMON_PREPOSITIONS.includes(word) ? word : _.capitalize(word)),
+ )
+ .join(' ');
const getFacetType = facet => {
if (_.includes(facet.field, 'datetime')) {
return 'datetime';
- } else if (facet.type === 'terms') {
+ } if (facet.type === 'terms') {
// on Annotations & Repo pages project_id is a terms facet
// need a way to force an *_id field to return terms
return 'terms';
- } else if (facet.type === 'exact') {
+ } if (facet.type === 'exact') {
return 'exact';
- } else if (
- _.some(['_id', '_uuid', 'md5sum', 'file_name'], idSuffix =>
- _.includes(facet.field, idSuffix),
- )
+ } if (
+ _.some([
+ '_id',
+ '_uuid',
+ 'md5sum',
+ 'file_name',
+ ], idSuffix => _.includes(facet.field, idSuffix),)
) {
return 'exact';
- } else if (facet.type === 'long') {
+ } if (facet.type === 'long') {
return 'range';
}
return 'terms';
@@ -68,6 +76,7 @@ const FacetWrapperDiv = styled.div({
});
const FacetWrapper = compose(
+ setDisplayName('EnhancedFacetWrapper'),
defaultProps({
onRequestRemove: _.noop,
isRemovable: false,
@@ -103,26 +112,26 @@ const FacetWrapper = compose(
exact: () => (
+ />
),
datetime: () => (
),
range: () => (
+ />
),
terms: () => (
+ />
),
}[facetType]();
const hasValueSearch =
@@ -140,18 +149,18 @@ const FacetWrapper = compose(
.length >= 20;
return (
-
+
+ showingValueSearch={showingValueSearch}
+ title={displayTitle}
+ />
{facetComponent}
);
diff --git a/src/packages/@ncigdc/components/GDCApps/APILink.js b/src/packages/@ncigdc/components/GDCApps/APILink.js
deleted file mode 100644
index 4d4b5556d..000000000
--- a/src/packages/@ncigdc/components/GDCApps/APILink.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import _ from 'lodash';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- {_.range(0, 11).map(x => )}
-
- API
-
-
-);
diff --git a/src/packages/@ncigdc/components/GDCApps/AppLink.js b/src/packages/@ncigdc/components/GDCApps/AppLink.js
new file mode 100644
index 000000000..17d171f9e
--- /dev/null
+++ b/src/packages/@ncigdc/components/GDCApps/AppLink.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import { range } from 'lodash';
+import { ExternalLink } from '@ncigdc/uikit/Links';
+import Wrapper from './LinkWrapper';
+
+const AppLink = ({
+ appName,
+ description,
+ drawnRange,
+ href,
+ imgSrc,
+ imgWidth,
+ title,
+ width,
+}) => (
+
+
+
+ {imgSrc && (
+
+ )}
+
+ {drawnRange && (
+
+ {range(0, drawnRange).map(n => )}
+
+ )}
+ {description}
+
+
+);
+
+export default AppLink;
diff --git a/src/packages/@ncigdc/components/GDCApps/DTTLink.js b/src/packages/@ncigdc/components/GDCApps/DTTLink.js
deleted file mode 100644
index 42bf859f8..000000000
--- a/src/packages/@ncigdc/components/GDCApps/DTTLink.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import _ from 'lodash';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- {_.range(0, 9).map(x => )}
-
- Data Transfer Tool
-
-
-);
diff --git a/src/packages/@ncigdc/components/GDCApps/DocsLink.js b/src/packages/@ncigdc/components/GDCApps/DocsLink.js
deleted file mode 100644
index c7a5ed04c..000000000
--- a/src/packages/@ncigdc/components/GDCApps/DocsLink.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import _ from 'lodash';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- {_.range(0, 15).map(x => )}
-
- Documentation
-
-
-);
diff --git a/src/packages/@ncigdc/components/GDCApps/GDCAppsDropdown.js b/src/packages/@ncigdc/components/GDCApps/GDCAppsDropdown.js
index 0bd899718..5465856e5 100644
--- a/src/packages/@ncigdc/components/GDCApps/GDCAppsDropdown.js
+++ b/src/packages/@ncigdc/components/GDCApps/GDCAppsDropdown.js
@@ -3,74 +3,148 @@
import React from 'react';
import Dropdown from '@ncigdc/uikit/Dropdown';
import styled from '@ncigdc/theme/styled';
-import PortalLink from '@ncigdc/components/GDCApps/PortalLink';
-import APILink from '@ncigdc/components/GDCApps/APILink';
-import WebsiteLink from '@ncigdc/components/GDCApps/WebsiteLink';
-import DTTLink from '@ncigdc/components/GDCApps/DTTLink';
-import SubmissionUILink from '@ncigdc/components/GDCApps/SubmissionUILink';
-import DocsLink from '@ncigdc/components/GDCApps/DocsLink';
-import LegacyLink from '@ncigdc/components/GDCApps/LegacyLink';
+import AppLink from '@ncigdc/components/GDCApps/AppLink';
+
+import dataPortalImg from '@ncigdc/theme/images/GDC-App-data-portal-blue.svg';
+import publicationsImg from '@ncigdc/theme/images/GDC-App-publications.svg';
+import websiteImg from '@ncigdc/theme/images/GDC-App-website-blue.svg';
import Row from '@ncigdc/uikit/Flex/Row';
-import Column from '@ncigdc/uikit/Flex/Column';
import './GDCAppsDropdown.css';
const DropDownButton = styled.span({
- padding: '15px 13px',
- display: 'block',
':hover': {
- cursor: 'pointer',
- color: '#333',
backgroundColor: '#dedddd',
+ color: '#333',
+ cursor: 'pointer',
},
+ display: 'block',
+ padding: '15px 13px',
});
const DropdownContent = styled(Row, {
+ justifyContent: 'space-between',
position: 'absolute',
- zIndex: 1,
textAlign: 'center',
- justifyContent: 'space-between',
width: 'initial',
+ zIndex: 1,
+});
+
+const Container = styled(Row, {
+ flexWrap: 'wrap',
+ justifyContent: 'space-between',
+ padding: '1.5rem 0 0',
+ width: '31rem',
});
const BUTTON_WIDTH = 26;
const GDCAppsDropdown = () => (
GDC Apps
- }
- >
+ )}
+ dropdownClassName="gdc-apps-menu-container"
+ dropdownStyle={{
+ borderBottomLeftRadius: '5px',
+ borderBottomRightRadius: '5px',
+ marginTop: '15px',
+ }}
+ style={{
+ border: 'none',
+ color: '#767676',
+ display: 'block',
+ margin: 0,
+ }}
+ >
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/src/packages/@ncigdc/components/GDCApps/GDCAppsRow.js b/src/packages/@ncigdc/components/GDCApps/GDCAppsRow.js
index ea293078d..41e84f24e 100644
--- a/src/packages/@ncigdc/components/GDCApps/GDCAppsRow.js
+++ b/src/packages/@ncigdc/components/GDCApps/GDCAppsRow.js
@@ -1,32 +1,94 @@
// @flow
import React from 'react';
import styled from '@ncigdc/theme/styled';
-import PortalLink from '@ncigdc/components/GDCApps/PortalLink';
-import APILink from '@ncigdc/components/GDCApps/APILink';
-import WebsiteLink from '@ncigdc/components/GDCApps/WebsiteLink';
-import DTTLink from '@ncigdc/components/GDCApps/DTTLink';
-import SubmissionUILink from '@ncigdc/components/GDCApps/SubmissionUILink';
-import DocsLink from '@ncigdc/components/GDCApps/DocsLink';
-import LegacyLink from '@ncigdc/components/GDCApps/LegacyLink';
+import AppLink from '@ncigdc/components/GDCApps/AppLink';
+
+import dataPortalImg from '@ncigdc/theme/images/GDC-App-data-portal-blue.svg';
+import publicationsImg from '@ncigdc/theme/images/GDC-App-publications.svg';
+import websiteImg from '@ncigdc/theme/images/GDC-App-website-blue.svg';
import { Row } from '@ncigdc/uikit/Flex';
const Container = styled(Row, {
- margin: '2rem 0',
justifyContent: 'space-around',
- width: '100vw',
+ margin: '2rem 0',
padding: '0 14rem',
+ width: '100vw',
});
const GDCAppsRow = () => (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/src/packages/@ncigdc/components/GDCApps/LegacyLink.js b/src/packages/@ncigdc/components/GDCApps/LegacyLink.js
deleted file mode 100644
index 01d7ae4f0..000000000
--- a/src/packages/@ncigdc/components/GDCApps/LegacyLink.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import _ from 'lodash';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- {_.range(0, 11).map(x => )}
-
- Legacy Archive
-
-
-);
diff --git a/src/packages/@ncigdc/components/GDCApps/LinkWrapper.js b/src/packages/@ncigdc/components/GDCApps/LinkWrapper.js
index 28bd445c2..96b4571b3 100644
--- a/src/packages/@ncigdc/components/GDCApps/LinkWrapper.js
+++ b/src/packages/@ncigdc/components/GDCApps/LinkWrapper.js
@@ -2,24 +2,26 @@ import styled from '@ncigdc/theme/styled';
import { Row } from '@ncigdc/uikit/Flex';
export default styled(Row, {
- whiteSpace: 'nowrap',
- display: 'inline !important',
- padding: '0.5rem',
- margin: '0.25rem',
- transition: 'background 0.25s ease-in-out',
- fontWeight: 'normal',
- backgroundColor: ({ theme, active }) =>
- active ? theme.greyScale6 : 'inherit',
+ ' .icon': {
+ fontSize: '2rem',
+ },
+
' a': {
color: '#333',
- fontWeight: 'normal',
- fontSize: 'small',
display: 'block',
+ fontSize: 'small',
+ fontWeight: 'normal',
},
- ' .icon': {
- fontSize: '2rem',
- },
+
':hover': {
backgroundColor: '#ededed',
},
+
+ backgroundColor: ({ active, theme }) => (
+ active ? theme.greyScale6 : 'inherit'),
+ fontWeight: 'normal',
+ justifyContent: 'center',
+ padding: '0.5rem',
+ transition: 'background 0.25s ease-in-out',
+ whiteSpace: 'nowrap',
});
diff --git a/src/packages/@ncigdc/components/GDCApps/PortalLink.js b/src/packages/@ncigdc/components/GDCApps/PortalLink.js
deleted file mode 100644
index 2c5accc03..000000000
--- a/src/packages/@ncigdc/components/GDCApps/PortalLink.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import dataPortalImg from '@ncigdc/theme/images/GDC-App-data-portal-blue.svg';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- Data Portal
-
-
-);
diff --git a/src/packages/@ncigdc/components/GDCApps/SubmissionUILink.js b/src/packages/@ncigdc/components/GDCApps/SubmissionUILink.js
deleted file mode 100644
index 6b1dea084..000000000
--- a/src/packages/@ncigdc/components/GDCApps/SubmissionUILink.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import _ from 'lodash';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- {_.range(0, 11).map(x => )}
-
- Data Submission Portal
-
-
-);
diff --git a/src/packages/@ncigdc/components/GDCApps/WebsiteLink.js b/src/packages/@ncigdc/components/GDCApps/WebsiteLink.js
deleted file mode 100644
index aed69162d..000000000
--- a/src/packages/@ncigdc/components/GDCApps/WebsiteLink.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import { ExternalLink } from '@ncigdc/uikit/Links';
-import websiteImg from '@ncigdc/theme/images/GDC-App-website-blue.svg';
-import Wrapper from './LinkWrapper';
-
-export default ({ width }) => (
-
-
-
- Website
-
-
-);
diff --git a/src/packages/@ncigdc/components/LocationSubscriber.js b/src/packages/@ncigdc/components/LocationSubscriber.js
index aa7ad8ef2..73f1a5a6a 100644
--- a/src/packages/@ncigdc/components/LocationSubscriber.js
+++ b/src/packages/@ncigdc/components/LocationSubscriber.js
@@ -3,8 +3,14 @@
import { parse } from 'query-string';
import withRouter from '@ncigdc/utils/withRouter';
-const LocationSubscriber = withRouter(({ location, children, push }) =>
- children({ ...location, query: parse(location.search), push }),
-);
+const LocationSubscriber = ({
+ children,
+ location,
+ push,
+}) => children({
+ ...location,
+ push,
+ query: parse(location.search),
+});
-export default LocationSubscriber;
+export default withRouter(LocationSubscriber);
diff --git a/src/packages/@ncigdc/components/Modals/AppendSetModal.js b/src/packages/@ncigdc/components/Modals/AppendSetModal.js
index 119475683..e3a5ef033 100644
--- a/src/packages/@ncigdc/components/Modals/AppendSetModal.js
+++ b/src/packages/@ncigdc/components/Modals/AppendSetModal.js
@@ -20,19 +20,19 @@ import onSaveComplete from './onSaveComplete';
const enhance = compose(
withState('selected', 'setSelected', ''),
- connect(({ sets, analysis }) => ({
+ connect(({ analysis, sets }) => ({
sets,
analysis,
})),
withProps(({
- sets, type, total, analysis,
+ analysis, sets, total, type,
}) => ({
sets: sets[type] || {},
analyses: analysis.saved || [],
})),
withRouter,
withCount(({
- field, type, scope, selected, filters,
+ field, filters, scope, selected, type,
}) => ({
key: 'countInBoth',
type,
@@ -52,7 +52,7 @@ const enhance = compose(
},
})),
withCount(({
- field, type, scope, selected, filters,
+ field, filters, scope, selected, type,
}) => ({
key: 'countExisting',
type,
@@ -73,26 +73,26 @@ const enhance = compose(
);
const AppendSetModal = ({
- total,
- title,
AppendSetButton,
- selected,
- setSelected,
- filters,
- sort,
- score,
+ analyses,
+ countExisting,
+ countInBoth,
dispatch,
- type,
displayType,
- sets,
field,
- query,
+ filters,
history,
location,
+ query,
+ score,
+ selected,
setInputTotal,
- countInBoth,
- countExisting,
- analyses,
+ setSelected,
+ sets,
+ sort,
+ title,
+ total,
+ type,
}) => {
const validating = selected && (countInBoth === -1 || countExisting === -1);
const nothingToAdd = !validating && total === countInBoth;
@@ -146,7 +146,7 @@ const AppendSetModal = ({
})
);
if (type === 'case') {
- analyses
+ await analyses
.filter(analysis => analysis.sets.case[selected])
.forEach(affected => {
dispatch(
diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx
new file mode 100644
index 000000000..a95995adc
--- /dev/null
+++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx
@@ -0,0 +1,502 @@
+// @flow
+/* eslint-disable */
+import React, { ReactNode } from 'react';
+/* eslint-enable */
+import { compose, withState, withProps } from 'recompose';
+import {
+ map, groupBy, reduce, filter, some,
+} from 'lodash';
+import { Row, Column } from '@ncigdc/uikit/Flex';
+import Button from '@ncigdc/uikit/Button';
+import { visualizingButton } from '@ncigdc/theme/mixins';
+import ControlEditableRow from '@ncigdc/uikit/ControlEditableRow';
+
+const styles = {
+ button: {
+ ...visualizingButton,
+ minWidth: 100,
+ },
+ horizonalPadding: {
+ paddingLeft: 20,
+ paddingRight: 20,
+ },
+};
+
+const initialName = (arr: string[], prefix: string) => {
+ /* @arr is the list of names
+ @ prefix is the prefix for the name
+ This function is to generate initial name for new file/list/element name.
+ e.g: if the arr is["new name 1", "new name 3", "apple", "banana"], prefix is "new name ".
+ Then the return value will be "new name 2".
+ */
+ const numberSet = new Set(arr);
+ for (let i = 1; i <= arr.length + 1; i += 1) {
+ if (!numberSet.has(prefix + i)) {
+ return prefix + i;
+ }
+ }
+ return prefix + arr.length + 1;
+};
+// type TOption = {
+// name: string,
+// };
+
+interface IBinProps {
+ key: string,
+ /* eslint-disable */
+ doc_count: number,
+ /* eslint-enable */
+ groupName: string,
+}
+
+interface ISelectedBinsProps {
+ [x: string]: boolean
+}
+interface IBinsProps { [x: string]: IBinProps }
+interface IGroupValuesModalProps {
+ binGrouping: () => void,
+ currentBins: IBinsProps,
+ dataBuckets: IBinProps[],
+ setCurrentBins: (currentBins: IBinsProps) => void,
+ onUpdate: (bins: IBinsProps) => void,
+ onClose: () => void,
+ fieldName: string,
+ selectedHidingBins: ISelectedBinsProps,
+ setSelectedHidingBins: (selectedHidingBins: ISelectedBinsProps) => void,
+ selectedGroupBins: ISelectedBinsProps,
+ setSelectedGroupBins: (selectedGroupBins: ISelectedBinsProps) => void,
+ editingGroupName: string,
+ setEditingGroupName: (editingGroupName: string) => void,
+ children?: ReactNode,
+ globalWarning: string,
+ setGlobalWarning: (globalWarning: string) => void,
+ setListWarning: (listWarning: { [x: string]: string }) => void,
+ listWarning: { [x: string]: string },
+}
+
+const blockStyle = {
+ height: '500px',
+ margin: '20px',
+ padding: '20px',
+ width: '40%',
+};
+
+const listStyle = {
+ borderRadius: '2px',
+ borderStyle: 'inset',
+ borderWidth: '2px',
+ height: '100%',
+ overflow: 'scroll',
+};
+
+const buttonStyle = {
+ float: 'right',
+ margin: '10px 2px 10px 3px',
+};
+export default compose(
+ withState('editingGroupName', 'setEditingGroupName', ''),
+ withState('currentBins', 'setCurrentBins', ({ bins }: { bins: IBinsProps }) => bins),
+ withState('selectedHidingBins', 'setSelectedHidingBins', {}),
+ withState('selectedGroupBins', 'setSelectedGroupBins', {}),
+ withState('globalWarning', 'setGlobalWarning', ''),
+ withState('listWarning', 'setListWarning', {}),
+ withProps(({
+ currentBins,
+ selectedGroupBins,
+ setCurrentBins,
+ setEditingGroupName,
+ setSelectedHidingBins,
+ }) => ({
+ binGrouping: () => {
+ const newGroupName = initialName(
+ Object.values(currentBins).map((bin: IBinProps) => bin.groupName), 'selected Value '
+ );
+ setEditingGroupName(newGroupName);
+ setCurrentBins({
+ ...currentBins,
+ ...reduce(selectedGroupBins, (acc, val, key) => {
+ if (val) {
+ return {
+ ...acc,
+ [key]: {
+ ...currentBins[key],
+ groupName: newGroupName,
+ },
+ };
+ }
+ return acc;
+ }, {}),
+ });
+ setSelectedHidingBins({});
+ },
+ }))
+)(
+ ({
+ binGrouping,
+ currentBins,
+ dataBuckets,
+ editingGroupName,
+ fieldName,
+ globalWarning,
+ listWarning,
+ onClose,
+ onUpdate,
+ selectedGroupBins,
+ selectedHidingBins,
+ setCurrentBins,
+ setGlobalWarning,
+ setListWarning,
+ setSelectedGroupBins,
+ setSelectedHidingBins,
+ }: IGroupValuesModalProps) => {
+ const groupNameMapping = groupBy(
+ Object.keys(currentBins)
+ .filter((bin: string) => currentBins[bin].groupName !== ''),
+ key => currentBins[key].groupName
+ );
+
+ return (
+
+
+ Create Custom Bins:
+ {' '}
+ {fieldName}
+
+
+ Organize values into groups of your choosing. Click Save Bins to udpate
+ the analysis plots.
+
+
+
+
+
+ Hidden Values
+
+
+
+ {Object.keys(currentBins)
+ .filter((binKey: string) => currentBins[binKey].groupName === '')
+ .map((binKey: string) => (
+ {
+ if (Object.keys(selectedGroupBins).length > 0) {
+ setSelectedGroupBins({});
+ }
+ setSelectedHidingBins({
+ ...selectedHidingBins,
+ [binKey]: !selectedHidingBins[binKey],
+ });
+ }}
+ style={{
+ backgroundColor: selectedHidingBins[binKey] ? '#d5f4e6' : '',
+ paddingLeft: '10px',
+ }}
+ >
+ {`${binKey} (${currentBins[binKey].doc_count})`}
+
+ ))}
+
+
+
+ !value)}
+ onClick={() => {
+ setCurrentBins({
+ ...currentBins,
+ ...reduce(selectedHidingBins, (acc, val, key) => {
+ if (val) {
+ return {
+ ...acc,
+ [key]: {
+ ...currentBins[key],
+ groupName: key,
+ },
+ };
+ }
+ return acc;
+ }, {}),
+ });
+ setSelectedHidingBins({});
+ setGlobalWarning('');
+ setListWarning({});
+ }}
+ style={{ margin: '10px' }}
+ >
+ {'>>'}
+
+ !value)}
+ onClick={() => {
+ if (filter(selectedGroupBins, Boolean).length ===
+ Object.keys(filter(currentBins, (bin: IBinProps) => !!bin.groupName)).length) {
+ setGlobalWarning('Leave at least one bin.');
+ return;
+ }
+ setCurrentBins({
+ ...currentBins,
+ ...reduce(selectedGroupBins, (acc, val, key) => {
+ if (val) {
+ return {
+ ...acc,
+ [key]: {
+ ...currentBins[key],
+ groupName: '',
+ },
+ };
+ }
+ return acc;
+ }, {}),
+ });
+ setSelectedGroupBins({});
+ setGlobalWarning('');
+ setListWarning({});
+ }}
+ style={{ margin: '10px' }}
+ >
+ {'<<'}
+
+
+
+
+
+ Displayed Values
+
+
+
+ {
+ setCurrentBins({
+ ...dataBuckets.reduce((acc, r) => ({
+ ...acc,
+ [r.key]: {
+ ...r,
+ groupName: r.key,
+ },
+ }), {}),
+ });
+ setSelectedGroupBins({});
+ setGlobalWarning('');
+ setListWarning({});
+ }}
+ style={buttonStyle}
+ >
+ {'Reset'}
+
+ selectedGroupBins[key])
+ .every(key => currentBins[key].groupName === key)}
+ onClick={() => {
+ setCurrentBins({
+ ...currentBins,
+ ...reduce(selectedGroupBins, (acc, val, key) => {
+ if (val) {
+ return {
+ ...acc,
+ [key]: {
+ ...currentBins[key],
+ groupName: key,
+ },
+ };
+ }
+ return acc;
+ }, {}),
+ });
+ setSelectedGroupBins({});
+ setGlobalWarning('');
+ setListWarning({});
+ }}
+ style={buttonStyle}
+ >
+ {'Ungroup'}
+
+ {
+ binGrouping();
+ setSelectedGroupBins({});
+ setGlobalWarning('');
+ setListWarning({});
+ }}
+ style={buttonStyle}
+ >
+ {'Group'}
+
+
+
+
+ {map(
+ groupNameMapping,
+ (group: string[], groupName: string) => (
+
+ {
+ if (Object.keys(selectedHidingBins).length > 0) {
+ setSelectedHidingBins({});
+ }
+ setSelectedGroupBins({
+ ...selectedGroupBins,
+ ...group.reduce((acc: ISelectedBinsProps, binKey: string) => ({
+ ...acc,
+ [binKey]: !group.every(
+ (binsWithSameGroupNameKey: string) => selectedGroupBins[binsWithSameGroupNameKey]
+ ),
+ }), {}),
+ });
+ }}
+ style={{
+ backgroundColor: group.every((binKey: string) => selectedGroupBins[binKey]) ? '#d5f4e6' : '',
+
+
+ }}
+ >
+ {group.length > 1 || group[0] !== groupName
+ ? (
+ setListWarning({})}
+ containerStyle={{
+ justifyContent: 'flex-start',
+ }}
+ disableOnKeyDown={listWarning[groupName]}
+ handleSave={(value: string) => {
+ if (listWarning[groupName]) {
+ return 'unsave';
+ }
+ setCurrentBins({
+ ...currentBins,
+ ...group.reduce((acc: ISelectedBinsProps, bin: string) => ({
+ ...acc,
+ [bin]: {
+ ...currentBins[bin],
+ groupName: value,
+ },
+ }), {}),
+ });
+ setGlobalWarning('');
+ setListWarning({});
+ setSelectedGroupBins({});
+ return null;
+ }
+ }
+ iconStyle={{
+ cursor: 'pointer',
+ fontSize: '1.8rem',
+ marginLeft: 10,
+ }}
+ isEditing={editingGroupName === groupName}
+ noEditingStyle={{ fontWeight: 'bold' }}
+ onEdit={(value: string) => {
+ if (value.trim() === '') {
+ setListWarning({
+ ...listWarning,
+ [groupName]: 'Can not be empty.',
+ });
+ } else if (
+ some(currentBins,
+ (bin: IBinProps) => bin.groupName.trim() === value.trim()) &&
+ groupName.trim() !== value.trim()
+ ) {
+ setListWarning({
+ ...listWarning,
+ [groupName]: `"${value.trim()}" already exists.`,
+ });
+ } else if (group.includes(value)) {
+ setListWarning({
+ ...listWarning,
+ [groupName]: 'Group name can\'t be the same as one of values.',
+ });
+ } else {
+ setListWarning({});
+ }
+ }}
+ text={groupName}
+ warning={listWarning[groupName]}
+ >
+ {groupName}
+
+ ) : (
+
+ {`${currentBins[group[0]].key} (${currentBins[group[0]].doc_count})`}
+
+ )
+ }
+
+ {
+ group.length > 1 || group[0] !== groupName
+ ? group.map((bin: string) => (
+ {
+ setSelectedGroupBins({
+ ...selectedGroupBins,
+ [bin]: !selectedGroupBins[bin],
+ });
+ }
+
+ }
+ style={{
+ backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '',
+ display: 'list-item',
+ listStylePosition: 'inside',
+ listStyleType: 'disc',
+ paddingLeft: '5px',
+ }}
+ >
+ {`${bin} (${currentBins[bin].doc_count})`}
+
+ ))
+ : null
+ }
+
+ )
+ )}
+
+
+
+
+ {globalWarning.length > 0 ? (
+
+ {'Warning: '}
+ {globalWarning}
+
+ ) : null}
+
+ Cancel
+
+ onUpdate(currentBins)}
+ style={styles.button}
+ >
+ Save Bins
+
+
+
+ );
+ }
+);
diff --git a/src/packages/@ncigdc/components/Modals/RemoveSetModal.js b/src/packages/@ncigdc/components/Modals/RemoveSetModal.js
index eb5551770..30a7022c4 100644
--- a/src/packages/@ncigdc/components/Modals/RemoveSetModal.js
+++ b/src/packages/@ncigdc/components/Modals/RemoveSetModal.js
@@ -1,6 +1,11 @@
// @flow
import React from 'react';
-import { compose, withState, withProps } from 'recompose';
+import {
+ compose,
+ setDisplayName,
+ withProps,
+ withState,
+} from 'recompose';
import { connect } from 'react-redux';
import { stringify } from 'query-string';
@@ -13,87 +18,88 @@ import { updateClinicalAnalysisSet } from '@ncigdc/dux/analysis';
import onSaveComplete from './onSaveComplete';
const enhance = compose(
- withState('selected', 'setSelected', ''),
- connect(({ sets, analysis }) => ({
- sets,
+ setDisplayName('EnhancedRemoveSetModal'),
+ withState('selected', 'setSelected', ({ selected }) => selected || ''),
+ connect(({ analysis, sets }) => ({
analysis,
+ sets,
})),
- withProps(({ sets, type, analysis }) => ({
- sets: sets[type] || {},
+ withProps(({ analysis, sets, type }) => ({
analyses: analysis.saved || [],
+ sets: sets[type] || {},
})),
withRouter
);
const RemoveSetModal = ({
- title,
- RemoveFromSetButton,
- selected,
- setSelected,
- filters,
+ analyses,
dispatch,
- type,
field,
- sets,
+ filters,
history,
query,
- analyses,
+ RemoveFromSetButton,
+ selected,
+ sets,
+ setSelected,
+ title,
+ type,
}) => (
- {
- if ((query.filters || '').includes(selected)) {
- history.replace({
- search: `?${stringify({
- ...query,
- filters: query.filters.replace(selected, setId),
- })}`,
- });
- }
-
- onSaveComplete({
- dispatch,
- label: sets[selected],
- });
- await dispatch(replaceSet({
- type,
- oldId: selected,
- newId: setId,
- }));
- if (type === 'case') {
- analyses
- .filter(analysis => analysis.sets.case[selected])
- .forEach(affected => {
- dispatch(
- updateClinicalAnalysisSet({
- id: affected.id,
- setId,
- setName: affected.sets.case[selected],
- })
- );
+ {
+ if ((query.filters || '').includes(selected)) {
+ history.replace({
+ search: `?${stringify({
+ ...query,
+ filters: query.filters.replace(selected, setId),
+ })}`,
});
- }
- }}
- set_id={`set_id:${selected}`}
- >
- Save
-
- )}
- title={title}
- >
-
-
-);
+ }
+
+ onSaveComplete({
+ dispatch,
+ label: sets[selected],
+ });
+ await dispatch(replaceSet({
+ newId: setId,
+ oldId: selected,
+ type,
+ }));
+ if (type === 'case') {
+ await analyses
+ .filter(analysis => analysis.sets.case[selected])
+ .forEach(affected => {
+ dispatch(
+ updateClinicalAnalysisSet({
+ id: affected.id,
+ setId,
+ setName: affected.sets.case[selected],
+ })
+ );
+ });
+ }
+ }}
+ set_id={`set_id:${selected}`}
+ >
+ Save
+
+ )}
+ title={title}
+ >
+
+
+ );
export default enhance(RemoveSetModal);
diff --git a/src/packages/@ncigdc/components/SearchPage.js b/src/packages/@ncigdc/components/SearchPage.js
index e62baf4f2..d871377ec 100644
--- a/src/packages/@ncigdc/components/SearchPage.js
+++ b/src/packages/@ncigdc/components/SearchPage.js
@@ -1,7 +1,11 @@
// @flow
import React from 'react';
-import { compose, withState } from 'recompose';
+import {
+ compose,
+ setDisplayName,
+ withState,
+} from 'recompose';
import { Row, Column } from '@ncigdc/uikit/Flex';
import styled from '@ncigdc/theme/styled';
@@ -46,7 +50,10 @@ type TProps = {
showRepositoryQuery: boolean,
};
-const enhance = compose(withState('showFacets', 'setShowFacets', true));
+const enhance = compose(
+ setDisplayName('EnhancedSearchPage'),
+ withState('showFacets', 'setShowFacets', true)
+);
const SearchPage = (
{
@@ -58,26 +65,26 @@ const SearchPage = (
...props
}: TProps = {},
) => (
-
+
{showFacets && (
{
setShowFacets(!showFacets);
}}
- aria-label="Toggle Facet Panel Visibility"
- >
+ style={{ minHeight: 46 }}
+ >
- }
- hideTabs={facetTabs.length <= 1}
- links={facetTabs}
- />
+ )}
+ />
)}
diff --git a/src/packages/@ncigdc/components/SetTable.js b/src/packages/@ncigdc/components/SetTable.js
index 6aa760679..312f9beac 100644
--- a/src/packages/@ncigdc/components/SetTable.js
+++ b/src/packages/@ncigdc/components/SetTable.js
@@ -1,7 +1,9 @@
// @flow
import React from 'react';
import { connect } from 'react-redux';
-import { withProps, compose, withState, defaultProps } from 'recompose';
+import {
+ withProps, compose, withState, defaultProps,
+} from 'recompose';
import EntityPageHorizontalTable from '@ncigdc/components/EntityPageHorizontalTable';
import countComponents from '@ncigdc/modern_components/Counts';
import withPropsOnChange from '@ncigdc/utils/withPropsOnChange';
@@ -25,15 +27,15 @@ const enhance = compose(
defaultProps({
getDisabledMessage: () => null,
}),
- withState('counts', 'setCounts', ({ sets }) =>
- Object.keys(sets).reduce(
- (acc, key) => Object.assign(acc, { [key]: '' }),
- {},
- ),
- ),
+ withState('counts', 'setCounts', ({ sets }) => Object.keys(sets).reduce(
+ (acc, key) => Object.assign(acc, { [key]: '' }),
+ {},
+ )),
withPropsOnChange(
['sets', 'counts'],
- ({ sets, selected, setSelected, counts, getDisabledMessage }) => {
+ ({
+ counts, getDisabledMessage, selected, setSelected, sets,
+ }) => {
const setKeys = Object.keys(sets);
if (
!selected &&
@@ -48,72 +50,82 @@ const enhance = compose(
);
const SetTable = ({
- sets,
- setSelected,
- selected,
- type,
- field,
- style,
counts,
- setCounts,
+ field,
getDisabledMessage,
+ selected,
+ setCounts,
+ setSelected,
+ sets,
+ style,
+ type,
}: TProps) => {
const CountComponent = countComponents[type];
return (
{
+ data={Object.keys(sets).sort((a, b) => (sets[a] > sets[b] ? 1 : -1)).map((key) => {
const id = `set-table-${key}-select`;
const disabledMessage = getDisabledMessage({ count: counts[key] });
return {
+ count: (
+ setCounts({
+ ...counts,
+ [key]: count,
+ })}
+ />
+ ),
+ name: {sets[key]} ,
select: (
+ >
setSelected(e.target.value)}
style={{
marginLeft: 3,
pointerEvents: disabledMessage ? 'none' : 'all',
}}
- id={id}
type="radio"
value={key}
- onChange={e => setSelected(e.target.value)}
- checked={key === selected}
- />
+ />
),
- name: {sets[key]} ,
- count: (
-
- setCounts({
- ...counts,
- [key]: count,
- })}
- filters={{
- op: '=',
- content: {
- field,
- value: `set_id:${key}`,
- },
- }}
- />
- ),
};
})}
headings={[
- { key: 'select', title: ' ' },
- { key: 'name', title: 'Name' },
- { key: 'count', title: 'Items', style: { textAlign: 'right' } },
+ {
+ key: 'select',
+ title: ' ',
+ },
+ {
+ key: 'name',
+ title: 'Name',
+ },
+ {
+ key: 'count',
+ style: { textAlign: 'right' },
+ title: 'Items',
+ },
]}
- />
+ style={style}
+ />
);
};
diff --git a/src/packages/@ncigdc/components/SurvivalPlotWrapper.js b/src/packages/@ncigdc/components/SurvivalPlotWrapper.js
index f7b0b8c0e..07a094b81 100644
--- a/src/packages/@ncigdc/components/SurvivalPlotWrapper.js
+++ b/src/packages/@ncigdc/components/SurvivalPlotWrapper.js
@@ -70,7 +70,7 @@ const colors = scaleOrdinal(schemeCategory10);
const styles = {
pValue: {
fontSize: '1.1rem',
- height: '1.5rem',
+ lineHeight: '1.5rem',
marginTop: '0.5rem',
textAlign: 'center',
},
@@ -94,7 +94,7 @@ const Container = ({
const SurvivalPlotWrapper = ({
height = 0,
- legend,
+ legend = [],
rawData,
setXDomain,
setSurvivalContainer,
@@ -111,7 +111,6 @@ const SurvivalPlotWrapper = ({
}: TProps) => {
const { results = [], overallStats = {} } = rawData || {};
const { pValue } = overallStats;
-
return (
the precision inherent in the code
- )
+ )
}
>
@@ -289,7 +288,7 @@ function renderSurvivalPlot(props: TProps): void {
onMouseEnterDonor: (
e,
{
- survivalEstimate, time = 0, censored, submitter_id, project_id,
+ censored, project_id, submitter_id, survivalEstimate, time = 0,
}
) => {
setTooltip(
diff --git a/src/packages/@ncigdc/components/UploadSetButton.js b/src/packages/@ncigdc/components/UploadSetButton.js
index 0618c69a7..64b3e8894 100644
--- a/src/packages/@ncigdc/components/UploadSetButton.js
+++ b/src/packages/@ncigdc/components/UploadSetButton.js
@@ -1,5 +1,9 @@
import React from 'react';
-import { compose, withProps } from 'recompose';
+import {
+ compose,
+ setDisplayName,
+ withProps,
+} from 'recompose';
import { connect } from 'react-redux';
import { get, truncate } from 'lodash';
import { Row } from '@ncigdc/uikit/Flex';
@@ -20,9 +24,10 @@ import Hidden from '@ncigdc/components/Hidden';
const MAX_LABEL_LENGTH = 30;
const enhance = compose(
+ setDisplayName('EnhancedUploadSetButton'),
connect(({ sets }) => ({ sets })),
withRouter,
- withProps(({ sets, type, query }) => {
+ withProps(({ query, sets, type }) => {
const currentFilters = parseFilterParam(query.filters, null);
return {
sets: sets[type] || {},
@@ -30,7 +35,10 @@ const enhance = compose(
currentFilters &&
(Array.isArray(currentFilters.content)
? currentFilters
- : { op: 'and', content: [currentFilters] }),
+ : {
+ op: 'and',
+ content: [currentFilters],
+ }),
};
}),
);
@@ -68,18 +76,18 @@ export default enhance(
return (
dispatch(setModal( ))}
style={{
padding: '4px 12px',
width: '100%',
borderRadius: '4px 0 0 4px',
}}
- onClick={() => dispatch(setModal( ))}
- >
+ >
{children}
+ >
See existing sets
- }
- >
+ )}
+ >
{Object.entries(sets).map(([setId, label]: [string, string]) => {
const value = `set_id:${setId}`;
return (
+ >
MAX_LABEL_LENGTH && (
{label}
)
}
- >
+ >
+ style={{
+ alignItems: 'center',
+ whiteSpace: 'nowrap',
+ }}
+ >
+ />
{truncate(label, { length: MAX_LABEL_LENGTH })}
@@ -145,7 +162,7 @@ export default enhance(
value: `set_id:${setId}`,
},
}}
- >
+ >
{count => (
{pluralize(
diff --git a/src/packages/@ncigdc/components/analysis/AnalysisResult.js b/src/packages/@ncigdc/components/analysis/AnalysisResult.js
index 8dec3a8f6..1927b5be2 100644
--- a/src/packages/@ncigdc/components/analysis/AnalysisResult.js
+++ b/src/packages/@ncigdc/components/analysis/AnalysisResult.js
@@ -1,7 +1,13 @@
import React from 'react';
-import { compose } from 'recompose';
+import {
+ compose,
+ setDisplayName,
+} from 'recompose';
import { connect } from 'react-redux';
-import { omit } from 'lodash';
+import {
+ omit,
+ truncate,
+} from 'lodash';
import withRouter from '@ncigdc/utils/withRouter';
import UnstyledButton from '@ncigdc/uikit/UnstyledButton';
@@ -13,12 +19,14 @@ import {
import { notify, closeNotification } from '@ncigdc/dux/notification';
import { Row, Column } from '@ncigdc/uikit/Flex';
import Button from '@ncigdc/uikit/Button';
+import { Tooltip } from '@ncigdc/uikit/Tooltip';
import { TrashIcon } from '@ncigdc/theme/icons';
import TabbedLinks from '@ncigdc/components/TabbedLinks';
import availableAnalysis from './availableAnalysis';
const enhance = compose(
+ setDisplayName('EnhancedAnalysisResult'),
connect(state => ({ analysis: state.analysis.saved })),
withRouter
);
@@ -33,14 +41,17 @@ function undoNotification(dispatch, analysis) {
{analysis.length === 1 ? (
{' '}
- Analysis{' '}
+ Analysis
+ {' '}
{availableAnalysis.find(a => analysis[0].type === a.type).label}
) : (
- {analysis.length} Analyses
+ {analysis.length}
+ {' '}
+ Analyses
)}
@@ -49,14 +60,14 @@ function undoNotification(dispatch, analysis) {
style={{
marginRight: '0.3rem',
}}
- />
+ />
{
analysis.map(set => dispatch(addAnalysis(set)));
dispatch(closeNotification());
}}
- >
+ style={{ textDecoration: 'underline' }}
+ >
Undo
@@ -67,7 +78,9 @@ function undoNotification(dispatch, analysis) {
}
// analysis here is all analyses in localStorage
-const AnalysisResult = ({ analysis, query, dispatch, push }) => {
+const AnalysisResult = ({
+ analysis, dispatch, push, query,
+}) => {
const analysisId = query.analysisId || '';
const currentIndex = Math.max(
analysis.findIndex(a => a.id === analysisId),
@@ -78,28 +91,7 @@ const AnalysisResult = ({ analysis, query, dispatch, push }) => {
analysisType === 'clinical_data' ? { minWidth: 1200 } : {};
return (
{
- dispatch(removeAllAnalysis());
- push({
- query: omit(query, 'analysisId'),
- });
- undoNotification(dispatch, analysis);
- }}
- >
- Delete All
-
- }
- linkStyle={{
- width: '100%',
- padding: '1rem 0.8rem',
- }}
links={analysis
.map(savedAnalysis => {
const analysis = availableAnalysis.find(
@@ -116,24 +108,33 @@ const AnalysisResult = ({ analysis, query, dispatch, push }) => {
text: (
-
-
+
+
-
- {_.truncate(tabTitle, { length: 16 })}
-
+ 16 ? tabTitle : null}>
+
+ {truncate(tabTitle, { length: 16 })}
+
+
{analysis.label}
{
e.preventDefault();
dispatch(removeAnalysis(savedAnalysis));
undoNotification(dispatch, [savedAnalysis]);
}}
- >
+ style={{
+ backgroundColor: 'transparent',
+ marginLeft: 'auto',
+ }}
+ >
x
@@ -144,7 +145,33 @@ const AnalysisResult = ({ analysis, query, dispatch, push }) => {
};
})
.filter(Boolean)}
- />
+ linkStyle={{
+ width: '100%',
+ padding: '1rem 0.8rem',
+ }}
+ queryParam="analysisId"
+ side
+ style={{
+ padding: '1rem 0.7rem',
+ ...tabMinWidth,
+ }}
+ tabToolbar={(
+ {
+ dispatch(removeAllAnalysis());
+ push({
+ query: omit(query, 'analysisId'),
+ });
+ undoNotification(dispatch, analysis);
+ }}
+ style={{ margin: '5px 5px 0 0' }}
+ >
+
+ {' '}
+ Delete All
+
+ )}
+ />
);
};
diff --git a/src/packages/@ncigdc/components/analysis/ClinicalAnalysisLaunch.tsx b/src/packages/@ncigdc/components/analysis/ClinicalAnalysisLaunch.tsx
index 3afef965f..b40f51b47 100644
--- a/src/packages/@ncigdc/components/analysis/ClinicalAnalysisLaunch.tsx
+++ b/src/packages/@ncigdc/components/analysis/ClinicalAnalysisLaunch.tsx
@@ -146,7 +146,7 @@ const ClinicalAnalysisLaunch: ComponentType = ({
Back
-
+
diff --git a/src/packages/@ncigdc/components/analysis/CreateAnalysis.js b/src/packages/@ncigdc/components/analysis/CreateAnalysis.js
index d36a48883..d1ae58c75 100644
--- a/src/packages/@ncigdc/components/analysis/CreateAnalysis.js
+++ b/src/packages/@ncigdc/components/analysis/CreateAnalysis.js
@@ -25,11 +25,13 @@ const enhance = compose(
))
),
withState('analysis', 'setAnalysis', null),
- connect(),
+ connect(state => ({
+ numAnalysis: state.analysis.saved.filter(analysis => analysis.type === 'clinical_data').length
+ })),
withRouter
);
-const CreateAnalysis = ({ analysis, setAnalysis, dispatch, push }) => {
+const CreateAnalysis = ({ analysis, dispatch, numAnalysis, push, setAnalysis }) => {
const SelectSetComponent =
analysis && analysis.type === 'clinical_data'
? ClinicalAnalysisLaunch
@@ -51,7 +53,7 @@ const CreateAnalysis = ({ analysis, setAnalysis, dispatch, push }) => {
sets,
...(analysis.type === 'clinical_data'
? {
- name: Object.values(sets.case)[0],
+ name: `Custom Analysis ${numAnalysis + 1}`,
displayVariables: defaultVariables,
}
: {}),
diff --git a/src/packages/@ncigdc/components/analysis/defaultCDAVEvariables.js b/src/packages/@ncigdc/components/analysis/defaultCDAVEvariables.js
index 8d98ab6bd..0f9fd0b07 100644
--- a/src/packages/@ncigdc/components/analysis/defaultCDAVEvariables.js
+++ b/src/packages/@ncigdc/components/analysis/defaultCDAVEvariables.js
@@ -5,7 +5,7 @@ export default {
active_calculation: 'number',
active_survival: 'overall',
plotTypes: 'categorical',
- bins: [],
+ bins: {},
scrollToCard: false,
},
@@ -15,7 +15,7 @@ export default {
active_calculation: 'number',
active_survival: 'overall',
plotTypes: 'categorical',
- bins: [],
+ bins: {},
scrollToCard: false,
},
@@ -34,17 +34,7 @@ export default {
active_calculation: 'number',
active_survival: 'overall',
plotTypes: 'continuous',
- bins: [],
- scrollToCard: false,
- },
-
- 'diagnoses.age_at_diagnosis': {
- type: 'Diagnosis',
- active_chart: 'histogram',
- active_calculation: 'number',
- active_survival: 'overall',
- plotTypes: 'continuous',
- bins: [],
+ bins: {},
scrollToCard: false,
},
'diagnoses.primary_diagnosis': {
@@ -53,7 +43,7 @@ export default {
active_calculation: 'number',
active_survival: 'overall',
plotTypes: 'categorical',
- bins: [],
+ bins: {},
scrollToCard: false,
},
};
diff --git a/src/packages/@ncigdc/containers/explore/ExplorePage.js b/src/packages/@ncigdc/containers/explore/ExplorePage.js
index fa76fade6..6762f9782 100644
--- a/src/packages/@ncigdc/containers/explore/ExplorePage.js
+++ b/src/packages/@ncigdc/containers/explore/ExplorePage.js
@@ -2,7 +2,6 @@
import React from 'react';
import Relay from 'react-relay/classic';
import { get, isEqual } from 'lodash';
-import { compose, lifecycle } from 'recompose';
import withRouter from '@ncigdc/utils/withRouter';
import SearchPage from '@ncigdc/components/SearchPage';
@@ -64,196 +63,209 @@ export type TProps = {
push: Function,
};
-function setVariables({ relay, filters }) {
+function setVariables({ filters, relay }) {
relay.setVariables({
cosmicFilters: replaceFilters(
{
- op: 'and',
content: [
- { op: 'not', content: { field: 'cosmic_id', value: ['MISSING'] } },
+ {
+ content: {
+ field: 'cosmic_id',
+ value: ['MISSING'],
+ },
+ op: 'not',
+ },
],
+ op: 'and',
},
filters
),
dbsnpRsFilters: replaceFilters(
{
- op: 'and',
content: [
{
- op: 'not',
content: {
field: 'consequence.transcript.annotation.dbsnp_rs',
value: ['MISSING'],
},
+ op: 'not',
},
],
+ op: 'and',
},
filters
),
});
}
-const enhance = compose(
- withRouter,
- lifecycle({
- componentDidMount() {
- setVariables(this.props);
- },
- componentWillReceiveProps(nextProps) {
- if (!isEqual(this.props.filters, nextProps.filters)) {
- setVariables(nextProps);
- }
- },
- })
-);
-export const ExplorePageComponent = ({
- viewer,
- filters,
- relay,
- push,
- theme,
-}: TProps) => (
-
- relay.setVariables(
- { idAutocompleteCases: value, runAutocompleteCases: !!value },
- onReadyStateChange
- )}
- />
- ),
- },
- {
- id: 'genes',
- text: 'Genes',
- component: (
-
- relay.setVariables(
- { idAutocompleteGenes: value, runAutocompleteGenes: !!value },
- onReadyStateChange
- )}
- />
- ),
- },
- {
- id: 'mutations',
- text: 'Mutations',
- component: (
-
- relay.setVariables(
- { idAutocompleteSsms: value, runAutocompleteSsms: !!value },
- onReadyStateChange
- )}
- />
- ),
- },
- ]}
- results={
-
-
- {filters ? (
- {
- push({
- pathname: '/repository',
- query: {
- filters: stringifyJSONParam({
- op: 'AND',
- content: [
- {
- op: 'IN',
- content: {
- field: 'cases.case_id',
- value: [`set_id:${setId}`],
- },
- },
- ],
- }),
+class ExplorePageComponent extends React.Component {
+ componentDidMount() {
+ setVariables(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (!isEqual(this.props.filters, nextProps.filters)) {
+ setVariables(nextProps);
+ }
+ }
+
+ render() {
+ const {
+ filters,
+ push,
+ relay,
+ viewer,
+ } = this.props;
+
+ return (
+ relay.setVariables(
+ {
+ idAutocompleteCases: value,
+ runAutocompleteCases: !!value,
},
- });
- }}
- >
- View Files in Repository
-
- ) : (
-
- push({
- pathname: '/repository',
- })}
- >
- View Files in Repository
-
- )}
-
-
- ) : (
- No Cases Found.
- ),
- },
- {
- id: 'genes',
- text: `Genes (${viewer.explore.genes.hits.total.toLocaleString()})`,
- component: viewer.explore.genes.hits.total ? (
-
- ) : (
- No Genes Found.
- ),
- },
- {
- id: 'mutations',
- text: `Mutations (${viewer.explore.ssms.hits.total.toLocaleString()})`,
- component: viewer.explore.ssms.hits.total ? (
-
+ ),
+ id: 'cases',
+ text: 'Cases',
+ },
+ {
+ component: (
+ relay.setVariables(
+ {
+ idAutocompleteGenes: value,
+ runAutocompleteGenes: !!value,
+ },
+ onReadyStateChange
+ )}
+ suggestions={get(viewer, 'autocomplete_genes.hits', [])}
+ />
+ ),
+ id: 'genes',
+ text: 'Genes',
+ },
+ {
+ component: (
+ relay.setVariables(
+ {
+ idAutocompleteSsms: value,
+ runAutocompleteSsms: !!value,
+ },
+ onReadyStateChange
+ )}
+ ssms={viewer.explore.ssms}
+ suggestions={get(viewer, 'autocomplete_ssms.hits', [])}
/>
+ ),
+ id: 'mutations',
+ text: 'Mutations',
+ },
+ ]}
+ results={(
+
+
+ {filters ? (
+ {
+ push({
+ pathname: '/repository',
+ query: {
+ filters: stringifyJSONParam({
+ content: [
+ {
+ content: {
+ field: 'cases.case_id',
+ value: [`set_id:${setId}`],
+ },
+ op: 'IN',
+ },
+ ],
+ op: 'AND',
+ }),
+ },
+ });
+ }}
+ style={{ marginBottom: '2rem' }}
+ >
+ View Files in Repository
+
) : (
- No Mutations Found.
- ),
- },
- {
- id: 'oncogrid',
- text: 'OncoGrid',
- component: ,
- },
- ]}
+ push({
+ pathname: '/repository',
+ })}
+ style={{ marginBottom: '2rem' }}
+ >
+ View Files in Repository
+
+ )}
+
+
+ ) : (
+ No Cases Found.
+ ),
+ id: 'cases',
+ text: `Cases (${viewer.explore.cases.hits.total.toLocaleString()})`,
+ },
+ {
+ component: viewer.explore.genes.hits.total ? (
+
+ ) : (
+ No Genes Found.
+ ),
+ id: 'genes',
+ text: `Genes (${viewer.explore.genes.hits.total.toLocaleString()})`,
+ },
+ {
+ component: viewer.explore.ssms.hits.total ? (
+
+ ) : (
+ No Mutations Found.
+ ),
+ id: 'mutations',
+ text: `Mutations (${viewer.explore.ssms.hits.total.toLocaleString()})`,
+ },
+ {
+ component: ,
+ id: 'oncogrid',
+ text: 'OncoGrid',
+ },
+ ]}
+ queryParam="searchTableTab"
+ />
+
+ )}
/>
-
- }
- />
-);
+ );
+ }
+}
export const ExplorePageQuery = {
initialVariables: {
@@ -362,7 +374,7 @@ export const ExplorePageQuery = {
};
const ExplorePage = Relay.createContainer(
- enhance(ExplorePageComponent),
+ withRouter(ExplorePageComponent),
ExplorePageQuery
);
diff --git a/src/packages/@ncigdc/dux/analysis.ts b/src/packages/@ncigdc/dux/analysis.ts
index 718bbb669..7feed9b0a 100644
--- a/src/packages/@ncigdc/dux/analysis.ts
+++ b/src/packages/@ncigdc/dux/analysis.ts
@@ -101,7 +101,7 @@ const defaultVariableConfig = {
active_chart: 'histogram',
active_calculation: 'number',
active_survival: 'overall',
- bins: [],
+ bins: {},
};
interface ICurrentAnalysis {
@@ -231,11 +231,11 @@ const reducer = (
...currentAnalysis.displayVariables,
[action.payload.fieldName as string]: {
...currentAnalysis.displayVariables[
- action.payload.fieldName as string
+ action.payload.fieldName as string
],
[action.payload
.variableKey as TClinicalAnalyisVariableKey]: action.payload
- .value,
+ .value,
},
},
},
diff --git a/src/packages/@ncigdc/dux/tableColumns.ts b/src/packages/@ncigdc/dux/tableColumns.ts
index b7c348f9b..444f714e6 100644
--- a/src/packages/@ncigdc/dux/tableColumns.ts
+++ b/src/packages/@ncigdc/dux/tableColumns.ts
@@ -46,7 +46,7 @@ const initialState = Object.keys(tableModels).reduce(
...acc,
[key]: tableModels[key],
}),
- { version: 4 }
+ { version: 5 }
);
const reducer = (state = initialState, action: ITableColumnsAction) => {
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js
index 11a09b912..3e11f2fca 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js
@@ -1,10 +1,11 @@
import React from 'react';
import {
compose,
- withState,
- withPropsOnChange,
- withProps,
+ setDisplayName,
withHandlers,
+ withProps,
+ withPropsOnChange,
+ withState,
} from 'recompose';
import { connect } from 'react-redux';
import SearchIcon from 'react-icons/lib/fa/search';
@@ -23,16 +24,13 @@ import {
} from '@ncigdc/theme/icons';
import CopyIcon from '@ncigdc/theme/icons/Copy';
import Hidden from '@ncigdc/components/Hidden';
-import { visualizingButton } from '@ncigdc/theme/mixins';
-import { zDepth1 } from '@ncigdc/theme/mixins';
+import { visualizingButton, zDepth1 } from '@ncigdc/theme/mixins';
+
import EntityPageHorizontalTable from '@ncigdc/components/EntityPageHorizontalTable';
-import ClinicalVariableCard from './ClinicalVariableCard';
-import ContinuousAggregation from './ContinuousAggregationQuery';
import Input from '@ncigdc/uikit/Form/Input';
import { withTheme } from '@ncigdc/theme';
import countComponents from '@ncigdc/modern_components/Counts';
import ExploreLink from '@ncigdc/components/Links/ExploreLink';
-import ControlPanelNode from './ControlPanelNode';
import {
updateClinicalAnalysisProperty,
addAnalysis,
@@ -51,6 +49,9 @@ import './survivalPlot.css';
// survival plot
import { getDefaultCurve } from '@ncigdc/utils/survivalplot';
import SurvivalPlotWrapper from '@ncigdc/components/SurvivalPlotWrapper';
+import ControlPanelNode from './ControlPanelNode';
+import ContinuousAggregation from './ContinuousAggregationQuery';
+import ClinicalVariableCard from './ClinicalVariableCard';
interface IAnalysisResultProps {
sets: any;
@@ -96,63 +97,69 @@ const styles = {
const plotTypes = {
categorical: ['histogram', 'survival'],
- continuous: ['histogram', 'survival', 'box'],
+ continuous: [
+ 'histogram',
+ 'survival',
+ 'box',
+ ],
};
const CopyAnalysisModal = compose(
+ setDisplayName('EnhancedCopyAnalysisModal'),
withState(
'modalInputValue',
'setModalInputValue',
({ analysis }) => `${analysis.name} copy`
)
-)(({ analysis, modalInputValue, setModalInputValue, dispatch, push }) => {
- return (
- {
- const created = new Date().toISOString();
- const id = created;
- dispatch(
- addAnalysis({
- ...analysis,
- id,
- created,
- name: modalInputValue,
- })
- ).then(() => {
- push({
- query: {
- analysisTableTab: 'result',
- analysisId: id,
- },
- });
+)(({
+ analysis, dispatch, modalInputValue, push, setModalInputValue,
+}) => (
+ dispatch(setModal(null))}>Cancel
+ }
+ onClose={() => {
+ const created = new Date().toISOString();
+ const id = created;
+ dispatch(
+ addAnalysis({
+ ...analysis,
+ id,
+ created,
+ name: modalInputValue,
+ })
+ ).then(() => {
+ push({
+ query: {
+ analysisTableTab: 'result',
+ analysisId: id,
+ },
});
- }}
- extraButtons={
- dispatch(setModal(null))}>Cancel
- }
+ });
+ }}
+ title="Copy Analysis"
>
-
- Please enter a name for the new analysis.
-
-
-
- {modalInputValue}
-
- setModalInputValue(e.target.value)}
- style={{ borderRadius: '4px' }}
- autoFocus
- onFocus={e => e.target.select()}
+
+ Please enter a name for the new analysis.
+
+
+
+ {modalInputValue}
+
+ setModalInputValue(e.target.value)}
+ onFocus={e => e.target.select()}
+ style={{ borderRadius: '4px' }}
+ value={modalInputValue}
/>
-
-
- );
-});
+
+
+));
const enhance = compose(
+ setDisplayName('EnhancedClinicalAnalysisResult'),
connect((state: any, props: any) => ({
allSets: state.sets,
currentAnalysis: state.analysis.saved.find(a => a.id === props.id),
@@ -170,14 +177,14 @@ const enhance = compose(
},
},
}) => ({
- parsedFacets: facets ? tryParseJSON(facets) : {},
hits,
+ parsedFacets: facets ? tryParseJSON(facets) : {},
})
),
withProps(
({
- setOverallSurvivalData,
currentAnalysis,
+ setOverallSurvivalData,
setState,
setSurvivalPlotLoading,
}) => ({
@@ -190,7 +197,7 @@ const enhance = compose(
{
op: '=',
content: {
- field: `cases.case_id`,
+ field: 'cases.case_id',
value: [`set_id:${setId}`],
},
},
@@ -214,36 +221,35 @@ const enhance = compose(
}
),
withHandlers({
- handleQueryInputChange: ({ setSearchValue }) => (event: any) =>
- setSearchValue(event.target.value),
+ handleQueryInputChange: ({ setSearchValue }) => (event: any) => setSearchValue(event.target.value),
}),
withTheme,
withRouter
);
const ClinicalAnalysisResult = ({
- sets,
- Icon,
- label,
allSets,
- theme,
+ clinicalAnalysisFields,
controlPanelExpanded,
- setControlPanelExpanded,
- variables,
- id,
- dispatch,
currentAnalysis,
- push,
- overallSurvivalData,
- populateSurvivalData,
- survivalPlotLoading,
- parsedFacets,
+ dispatch,
displayVariables,
- clinicalAnalysisFields,
+ handleQueryInputChange,
hits,
+ Icon,
+ id,
+ label,
+ overallSurvivalData,
+ parsedFacets,
+ populateSurvivalData,
+ push,
searchValue,
+ setControlPanelExpanded,
+ sets,
setSearchValue,
- handleQueryInputChange,
+ survivalPlotLoading,
+ theme,
+ variables,
...props
}: IAnalysisResultProps) => {
const setId = Object.keys(currentAnalysis.sets.case)[0];
@@ -252,11 +258,11 @@ const ClinicalAnalysisResult = ({
if (hits.total === 0) {
return (
+ />
);
}
return (
@@ -267,36 +273,49 @@ const ClinicalAnalysisResult = ({
justifyContent: 'space-between',
padding: 10,
}}
- >
-
-
+ >
+
+
-
+
- dispatch(
- updateClinicalAnalysisProperty({
- value: _.trim(value),
- property: 'name',
- id,
- })
- )
+ disabledMessage="Editing analysis name is not available in demo mode"
+ handleSave={value => dispatch(
+ updateClinicalAnalysisProperty({
+ value: _.trim(value),
+ property: 'name',
+ id,
+ })
+ )
}
iconStyle={{
marginLeft: 10,
fontSize: '1.8rem',
cursor: 'pointer',
}}
- containerStyle={{ justifyContent: 'flex-start' }}
- >
-
- {currentAnalysis.name}{' '}
+ text={currentAnalysis.name}
+ >
+
+ {`${currentAnalysis.name} `}
@@ -304,8 +323,9 @@ const ClinicalAnalysisResult = ({
{label}
-
+
}
onClick={() => {
dispatch(
setModal(
@@ -313,22 +333,24 @@ const ClinicalAnalysisResult = ({
analysis={currentAnalysis}
dispatch={dispatch}
push={push}
- />
+ />
)
);
}}
- leftIcon={ }
- >
+ >
Copy Analysis
Print}>
{
window.print();
}}
- >
+ style={{
+ ...visualizingButton,
+ height: '100%',
+ }}
+ >
Print
@@ -338,16 +360,17 @@ const ClinicalAnalysisResult = ({
{!controlPanelExpanded && (
-
+
setControlPanelExpanded(!controlPanelExpanded)}
- />
+ style={styles.collapseIcon}
+ />
)}
{controlPanelExpanded && (
+ >
-
+
setControlPanelExpanded(!controlPanelExpanded)}
- />
+ style={styles.collapseIcon}
+ />
+ >
Cohort
- {`# Cases`}
+ # Cases
+ >
+ disabledMessage="Switching cohorts is not available in demo mode"
+ dispatch={dispatch}
+ sets={allSets}
+ />
+ >
+ />
+ >
Search
@@ -435,24 +455,29 @@ const ClinicalAnalysisResult = ({
+ value={searchValue}
+ />
+ usefulFacets={getUsefulFacets(parsedFacets)}
+ />
)}
-
+
{/*
*/}
+ >
+ >
+ >
+ >
Overall Survival
@@ -531,64 +556,67 @@ const ClinicalAnalysisResult = ({
height: '250px',
margin: '5px 2px 10px',
}}
- >
+ >
+ uniqueClass="clinical-survival-plot"
+ />
{_.map(displayVariables, (varProperties, varFieldName) => {
const filters = {
- op: 'and',
content: [
{
- op: '=',
content: {
- field: `cases.case_id`,
+ field: 'cases.case_id',
value: [`set_id:${setId}`],
},
+ op: '=',
},
],
+ op: 'and',
};
if (varProperties.plotTypes === 'continuous') {
return (
+ plots={plotTypes[varProperties.plotTypes || 'categorical']}
+ setId={setId}
+ stats={parsedFacets[varFieldName].stats}
+ style={{ minWidth: controlPanelExpanded ? 310 : 290 }}
+ variable={varProperties}
+ />
);
}
return (
+ plots={plotTypes[varProperties.plotTypes || 'categorical']}
+ setId={setId}
+ style={{ minWidth: controlPanelExpanded ? 310 : 290 }}
+ variable={varProperties}
+ />
);
})}
@@ -598,4 +626,4 @@ const ClinicalAnalysisResult = ({
);
};
-export default enhance(ClinicalAnalysisResult);
+export default enhance(ClinicalAnalysisResult);
\ No newline at end of file
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.relay.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.relay.js
index 50a560a4c..3957b8856 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.relay.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.relay.js
@@ -2,73 +2,71 @@ import React from 'react';
import Query from '@ncigdc/modern_components/Query';
import { graphql } from 'react-relay';
import {
- compose,
- withPropsOnChange,
branch,
+ compose,
renderComponent,
+ setDisplayName,
withProps,
+ withPropsOnChange,
} from 'recompose';
import { connect } from 'react-redux';
import DeprecatedSetResult from './DeprecatedSetResult';
-export default (Component: ReactClass<*>) =>
- compose(
- connect((state: any, props: any) => ({
- currentAnalysis: state.analysis.saved.find(a => a.id === props.id),
- allSets: state.sets,
- })),
- withProps(({ currentAnalysis, allSets }) => ({
- currentSetId: Object.keys(currentAnalysis.sets.case)[0],
- })),
- branch(
- ({ currentSetId, allSets }) =>
- !currentSetId.includes('demo') && !allSets.case[currentSetId],
- renderComponent(({ currentAnalysis, allSets, dispatch, Icon }) => {
- return (
-
- );
- })
- ),
- withPropsOnChange(
- ['clinicalAnalysisFields', 'currentAnalysis'],
- ({ clinicalAnalysisFields, currentAnalysis }) => {
- const facets = clinicalAnalysisFields
- .map(field => field.name.replace(/__/g, '.'))
- .join(',');
- const setId = Object.keys(currentAnalysis.sets.case)[0];
- return {
- variables: {
- filters: {
- op: 'and',
- content: [
- {
- op: '=',
- content: {
- field: `cases.case_id`,
- value: [`set_id:${setId}`],
- },
+export default (Component: ReactClass<*>) => compose(
+ setDisplayName('EnhancedClinicalAnalysisResult_Relay'),
+ connect((state: any, props: any) => ({
+ allSets: state.sets,
+ currentAnalysis: state.analysis.saved.find(a => a.id === props.id),
+ })),
+ withProps(({ currentAnalysis }) => ({
+ currentSetId: Object.keys(currentAnalysis.sets.case)[0],
+ })),
+ branch(
+ ({ allSets, currentSetId }) => !currentSetId.includes('demo') && !allSets.case[currentSetId],
+ renderComponent(({
+ allSets, currentAnalysis, dispatch, Icon,
+ }) => (
+
+ ))
+ ),
+ withPropsOnChange(
+ ['clinicalAnalysisFields', 'currentAnalysis'],
+ ({ clinicalAnalysisFields, currentAnalysis }) => {
+ const facets = clinicalAnalysisFields
+ .map(field => field.name.replace(/__/g, '.'))
+ .join(',');
+ const setId = Object.keys(currentAnalysis.sets.case)[0];
+ return {
+ variables: {
+ facets,
+ filters: {
+ content: [
+ {
+ content: {
+ field: 'cases.case_id',
+ value: [`set_id:${setId}`],
},
- ],
- },
- facets,
+ op: '=',
+ },
+ ],
+ op: 'and',
},
- };
- }
- )
- )((props: Object) => {
- return (
- (
+ ) =>
}
}
}
- }
- `}
+ }`}
+ variables={props.variables}
/>
- );
- });
+));
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js
deleted file mode 100644
index e58c69811..000000000
--- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js
+++ /dev/null
@@ -1,1232 +0,0 @@
-import React from 'react';
-import {
- compose,
- lifecycle,
- setDisplayName,
- withProps,
- withPropsOnChange,
- withState,
-} from 'recompose';
-import DownCaretIcon from 'react-icons/lib/fa/caret-down';
-import { connect } from 'react-redux';
-import {
- find,
- isEmpty,
- isEqual,
- min,
- map,
- max,
- reject,
- truncate,
-} from 'lodash';
-import { scaleOrdinal, schemeCategory10 } from 'd3';
-
-import { Row, Column } from '@ncigdc/uikit/Flex';
-import Button from '@ncigdc/uikit/Button';
-import { Tooltip } from '@ncigdc/uikit/Tooltip';
-import { visualizingButton, zDepth1 } from '@ncigdc/theme/mixins';
-
-import EntityPageHorizontalTable from '@ncigdc/components/EntityPageHorizontalTable';
-import Dropdown from '@ncigdc/uikit/Dropdown';
-import DropdownItem from '@ncigdc/uikit/DropdownItem';
-import Hidden from '@ncigdc/components/Hidden';
-import BarChart from '@ncigdc/components/Charts/BarChart';
-import ExploreLink from '@ncigdc/components/Links/ExploreLink';
-import { makeFilter, addInFilters } from '@ncigdc/utils/filters';
-import { CreateExploreCaseSetButton, AppendExploreCaseSetButton, RemoveFromExploreCaseSetButton } from '@ncigdc/modern_components/withSetAction';
-
-
-import { setModal } from '@ncigdc/dux/modal';
-import SaveSetModal from '@ncigdc/components/Modals/SaveSetModal';
-import AppendSetModal from '@ncigdc/components/Modals/AppendSetModal';
-import RemoveSetModal from '@ncigdc/components/Modals/RemoveSetModal';
-import DownloadVisualizationButton from '@ncigdc/components/DownloadVisualizationButton';
-import wrapSvg from '@ncigdc/utils/wrapSvg';
-import './survivalPlot.css';
-import { downloadToTSV } from '@ncigdc/components/DownloadTableToTsvButton';
-
-// survival plot
-import {
- getSurvivalCurvesArray,
-} from '@ncigdc/utils/survivalplot';
-import SurvivalPlotWrapper from '@ncigdc/components/SurvivalPlotWrapper';
-import {
- SpinnerIcon,
- CloseIcon,
- SurvivalIcon,
- BarChartIcon,
- BoxPlot,
-} from '@ncigdc/theme/icons';
-
-import { withTheme } from '@ncigdc/theme';
-
-import {
- removeClinicalAnalysisVariable,
- updateClinicalAnalysisVariable,
-} from '@ncigdc/dux/analysis';
-import { humanify } from '@ncigdc/utils/string';
-import { getLowerAgeYears, getUpperAgeYears } from '@ncigdc/utils/ageDisplay';
-import timestamp from '@ncigdc/utils/timestamp';
-
-import { IS_CDAVE_DEV } from '@ncigdc/utils/constants';
-import { MAXIMUM_CURVES, MINIMUM_CASES } from '../../utils/survivalplot';
-
-const colors = scaleOrdinal(schemeCategory10);
-
-const makeWrapperId = fieldName => {
- const fieldArr = fieldName.split('.');
- const fieldTerm = fieldArr[1] === 'treatments' ? fieldArr[2] : fieldArr[1];
- return `${fieldTerm}-chart`;
-};
-
-interface ITableHeading {
- key: string;
- title: string;
- style?: React.CSSProperties;
-}
-
-type TPlotType = 'categorical' | 'continuous';
-type TActiveChart = 'box' | 'survival' | 'histogram';
-type TActiveCalculation = 'number' | 'percentage';
-type TVariableType =
- | 'Demographic'
- | 'Diagnosis'
- | 'Exposure'
- | 'Treatment'
- | 'Follow_up' // confirm type name
- | 'Molecular_test'; // confirm type name
-
-interface IVariable {
- bins: any[]; // tbd - bins still need spec
- plotTypes: TPlotType;
- active_chart: TActiveChart;
- active_calculation: TActiveCalculation;
- type: TVariableType;
-}
-
-interface IVariableCardProps {
- variable: IVariable;
- fieldName: string;
- plots: any[];
- style: React.CSSProperties;
- theme: IThemeProps;
- dispatch: (arg: any) => void;
- id: string;
- survivalData: any[];
-}
-
-interface IVizButton {
- title: string;
- icon: JSX.Element;
- action: (
- payload: IAnalysisPayload
- ) => { type: string, payload: IAnalysisPayload };
-}
-
-interface IVizButtons {
- survival: IVizButton;
- histogram: IVizButton;
- box: IVizButton;
- delete: IVizButton;
-}
-
-const CHART_HEIGHT = 250;
-const vizButtons: IVizButtons = {
- survival: {
- title: 'Survival Plot',
- icon: ,
- action: updateClinicalAnalysisVariable,
- },
- histogram: {
- title: 'Histogram',
- icon: ,
- action: updateClinicalAnalysisVariable,
- },
- box: {
- title: 'Box/QQ Plot',
- icon: ,
- action: updateClinicalAnalysisVariable,
- },
- delete: {
- title: 'Remove Card',
- icon: ,
- action: removeClinicalAnalysisVariable,
- },
-};
-
-const styles = {
- common: (theme: IThemeProps) => ({
- backgroundColor: 'transparent',
- color: theme.greyScale2,
- border: `1px solid ${theme.greyScale4}`,
- justifyContent: 'flex-start',
- ':hover': {
- backgroundColor: 'rgb(0,138,224)',
- color: '#fff',
- border: '1px solid rgb(0,138,224)',
- },
- }),
- activeButton: (theme: IThemeProps) => ({
- ...styles.common(theme),
- color: '#fff',
- backgroundColor: theme.primary,
- border: `1px solid ${theme.primary}`,
- }),
- histogram: (theme: IThemeProps) => ({
- axis: {
- textFill: theme.greyScale3,
- fontSize: '0.9rem',
- fontWeight: '500',
- stroke: theme.greyScale4,
- },
- }),
- actionMenuItem: {
- lineHeight: '1.5',
- cursor: 'pointer',
- },
-};
-
-const valueIsDays = str => /(days_to|age_at)/.test(str);
-const valueIsYear = str => /year_of/.test(str);
-
-const getRangeValue = (key, field, nextInterval) => {
- if (valueIsDays(field)) {
- return `${getLowerAgeYears(key)}${
- nextInterval === 0 ? '+' : ` - ${getUpperAgeYears(nextInterval - 1)}`
- } years`;
- } if (valueIsYear(field)) {
- return `${Math.floor(key)}${
- nextInterval === 0 ? ' - present' : ` - ${nextInterval - 1}`
- }`;
- }
- return key;
-};
-
-const getCountLink = ({ doc_count, filters, totalDocs }) => (
-
-
- {(doc_count || 0).toLocaleString()}
-
- {` (${(((doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`}
-
-);
-
-const ClinicalVariableCard: React.ComponentType = ({
- variable,
- fieldName,
- plots,
- style = {},
- theme,
- dispatch,
- id,
- setId,
- overallSurvivalData,
- survivalPlotLoading,
- selectedSurvivalLoadingIds,
- selectedSurvivalData,
- selectedSurvivalValues,
- updateSelectedSurvivalValues,
- filters,
- selectedBuckets,
- setSelectedBuckets,
- rawQueryData,
- getBucketRangesAndFilters,
- totalDocs,
- currentAnalysis,
-}) => {
- const getCategoricalTableData = (rawData, type) => {
- if (isEmpty(rawData)) {
- return [];
- }
-
- const displayData =
- type === 'continuous'
- ? rawData
- .sort((a, b) => b.key - a.key)
- .reduce(getBucketRangesAndFilters, {
- nextInterval: 0,
- data: [],
- })
- .data.slice(0)
- .reverse()
- : rawData
- .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing'))
- .map(b => ({
- ...b,
- key: b.key,
- chart_doc_count: b.doc_count,
- doc_count: getCountLink({
- doc_count: b.doc_count,
- filters:
- b.key === '_missing'
- ? {
- op: 'AND',
- content: [
- {
- op: 'IS',
- content: {
- field: fieldName,
- value: [b.key],
- },
- },
- {
- op: 'in',
- content: {
- field: 'cases.case_id',
- value: `set_id:${setId}`,
- },
- },
- ],
- }
- : makeFilter([
- {
- field: 'cases.case_id',
- value: `set_id:${setId}`,
- },
- {
- field: fieldName,
- value: [b.key],
- },
- ]),
- totalDocs,
- }),
- }));
-
- return displayData.map(b => ({
- ...b,
- select: (
- {
- if (find(selectedBuckets, { key: b.key })) {
- setSelectedBuckets(
- reject(selectedBuckets, r => r.key === b.key)
- );
- } else {
- setSelectedBuckets([...selectedBuckets, b]);
- }
- }}
- style={{
- marginLeft: 3,
- pointerEvents: 'initial',
- }}
- type="checkbox"
- value={b.key}
- />
- ),
- ...(variable.active_chart === 'survival'
- ? {
- survival: (
- -1
- ? `Click icon to remove ${b.key}`
- : selectedSurvivalValues.length < MAXIMUM_CURVES
- ? `Click icon to plot ${b.key}`
- : `Maximum plots (${MAXIMUM_CURVES}) reached`
- }
- >
- = MAXIMUM_CURVES &&
- selectedSurvivalValues.indexOf(b.key) === -1)
- }
- onClick={() => {
- updateSelectedSurvivalValues(displayData, b);
- }}
- style={{
- padding: '2px 3px',
- backgroundColor:
- selectedSurvivalValues.indexOf(b.key) === -1
- ? '#666'
- : colors(selectedSurvivalValues.indexOf(b.key)),
- color: 'white',
- margin: '0 auto',
- position: 'static',
- opacity:
- b.key === '_missing' ||
- b.chart_doc_count < MINIMUM_CASES ||
- (selectedSurvivalValues.length >= MAXIMUM_CURVES &&
- selectedSurvivalValues.indexOf(b.key) === -1)
- ? '0.33'
- : '1',
- }}
- >
- {selectedSurvivalLoadingIds.indexOf(b.key) !== -1 ? (
-
- ) : (
-
- )}
-
-
- ),
- }
- : {}),
- }));
- };
-
- const getBoxTableData = rawData => {
- if (isEmpty(rawData)) {
- return {};
- }
- // TODO: what num format for stat counts?
- return map((rawData || { stats: {} }).stats, (count, stat) => {
- return {
- stat,
- count,
- };
- });
- };
-
- const tableData =
- variable.active_chart === 'box'
- ? getBoxTableData([])
- : getCategoricalTableData(rawQueryData, variable.plotTypes);
-
- const noDataTotal =
- totalDocs - rawQueryData.reduce((acc, bucket) => acc + bucket.doc_count, 0);
-
- const devData = [
- ...tableData,
- ...(noDataTotal > 0
- ? [
- {
- select: '',
- key: 'Not in Index',
- doc_count: (
-
- {(noDataTotal || 0).toLocaleString()}
- {` (${(((noDataTotal || 0) / totalDocs) * 100).toFixed(2)}%)`}
-
- ),
- survival: '',
- },
- ]
- : []),
- {
- select: '',
- key: 'Total',
- doc_count: totalDocs,
- survival: '',
- },
- ];
-
- const getHeadings = chartType => {
- return chartType === 'box'
- ? [
- {
- key: 'stat',
- title: 'Stat',
- },
- {
- key: 'count',
- title: 'Value',
- style: { textAlign: 'right' },
- },
- ]
- : [
- {
- key: 'select',
- title: 'Select',
- thStyle: {
- position: 'sticky',
- top: 0,
- },
- },
- {
- key: 'key',
- title: humanify({ term: fieldName }),
- thStyle: {
- position: 'sticky',
- top: 0,
- },
- },
- {
- key: 'doc_count',
- title: '# Cases',
- style: { textAlign: 'right' },
- thStyle: {
- position: 'sticky',
- top: 0,
- textAlign: 'right',
- },
- },
- ...(chartType === 'survival' ? [
- {
- key: 'survival',
- title: 'Survival',
- style: {
- display: 'flex',
- justifyContent: 'flex-end',
- },
- thStyle: {
- position: 'sticky',
- top: 0,
- textAlign: 'right',
- },
- },
- ] : []),
- ];
- };
-
- const chartData =
- variable.active_chart === 'histogram'
- ? tableData.map(d => {
- return {
- label: truncate(d.key, { length: 18 }),
- value:
- variable.active_calculation === 'number'
- ? d.chart_doc_count
- : (d.chart_doc_count / totalDocs) * 100,
-
- tooltip: `${d.key}: ${d.chart_doc_count.toLocaleString()}`,
- };
- })
- : [];
-
- // set action will default to cohort total when no buckets are selected
- const totalFromSelectedBuckets = selectedBuckets.length
- ? selectedBuckets.reduce((acc, b) => acc + b.chart_doc_count, 0)
- : totalDocs;
-
- const getContinuousSetFilters = () => {
- const bucketRanges = selectedBuckets.map(b => b.rangeValues);
-
- if (bucketRanges.length === 1 && bucketRanges[0].max === -1) {
- return addInFilters(filters, {
- op: 'and',
- content: [
- {
- op: '>=',
- content: {
- field: fieldName,
- value: [bucketRanges[0].min],
- },
- },
- ],
- });
- }
-
- return addInFilters(filters, {
- op: 'and',
- content: [
- {
- op: '>=',
- content: {
- field: fieldName,
- value: [min(bucketRanges.map(range => range.min))],
- },
- },
- {
- op: '<=',
- content: {
- field: fieldName,
- value: [max(bucketRanges.map(range => range.max))],
- },
- },
- ],
- });
- };
-
- const getCategoricalSetFilters = () => {
- let bucketFilters = filters;
- if (
- selectedBuckets.filter(bucket => bucket.key !== '_missing').length > 0
- ) {
- bucketFilters = addInFilters(bucketFilters, {
- op: 'and',
- content: [
- {
- op: 'in',
- content: {
- field: fieldName,
- value: selectedBuckets
- .filter(bucket => bucket.key !== '_missing')
- .map(selectedBucket => selectedBucket.key),
- },
- },
- ],
- });
- }
-
- if (find(selectedBuckets, bucket => bucket.key === '_missing')) {
- bucketFilters = addInFilters(bucketFilters, {
- op: 'and',
- content: [
- {
- op: 'is',
- content: {
- field: fieldName,
- value: 'MISSING',
- },
- },
- ],
- });
- }
- return bucketFilters;
- };
- let cardFilters = filters;
- if (selectedBuckets.length) {
- cardFilters =
- variable.plotTypes === 'continuous'
- ? getContinuousSetFilters()
- : getCategoricalSetFilters();
- }
-
- const wrapperId = makeWrapperId(fieldName);
-
- const tsvSubstring = fieldName.replace(/\./g, '-');
-
- // SURVIVAL PLOT STUFF
-
- return (
-
-
-
- {humanify({ term: fieldName })}
-
-
- {plots.concat('delete')
- .map(plotType => (
-
- {
- dispatch(
- vizButtons[plotType].action({
- fieldName,
- variableKey: 'active_chart',
- value: plotType,
- id,
- })
- );
- }}
- style={{
- ...(plotType === variable.active_chart
- ? styles.activeButton(theme)
- : styles.common(theme)),
- margin: 2,
- }}
- >
- {vizButtons[plotType].title}
- {vizButtons[plotType].icon}
-
-
- ))}
-
-
- {isEmpty(tableData)
- ? (
-
- There is no data for this facet
-
- )
- : (
-
-
- {variable.active_chart !== 'survival' && (
-
- )}
-
- {/* {variable.active_chart === 'survival' && (
-
-
-
- Log Rank Test P=Value ={' '}
-
-
- )} */}
-
- {variable.active_chart === 'histogram' && (
-
- )}
- {variable.active_chart === 'survival' && (
-
- {selectedSurvivalValues.length === 0 ? (
-
- ) : (
-
- )}
-
- )}
- {variable.active_chart === 'box' && (
-
- {variable.active_chart}
-
- )}
-
-
- }
- style={{
- ...visualizingButton,
- padding: '0 12px',
- }}
- >
- Select action
-
- )}
- dropdownStyle={{
- left: 0,
- minWidth: 205,
- }}
- >
- downloadToTSV({
- selector: `#analysis-${tsvSubstring}-table`,
- filename: `analysis-${
- currentAnalysis.name
- }-${tsvSubstring}.${timestamp()}.tsv`,
- })
- }
- style={styles.actionMenuItem}
- >
- Export to TSV
-
- {
- dispatch(
- setModal(
-
- )
- );
- }}
- style={styles.actionMenuItem}
- >
- Save as new case set
-
- {
- dispatch(
- setModal(
-
- )
- );
- }}
- style={styles.actionMenuItem}
- >
- Add to existing case set
-
- {
- dispatch(
- setModal(
-
- )
- );
- }}
- style={styles.actionMenuItem}
- >
- Remove from existing case set
-
-
- }
- style={{
- ...visualizingButton,
- padding: '0 12px',
- }}
- >
- Customize Bins
-
-
-
-
-
- )}
-
- );
-};
-
-export default compose(
- setDisplayName('EnhancedClinicalVariableCard'),
- connect((state: any) => ({ analysis: state.analysis })),
- withTheme,
- withState('selectedSurvivalData', 'setSelectedSurvivalData', {}),
- withState('selectedSurvivalValues', 'setSelectedSurvivalValues', []),
- withState('selectedSurvivalLoadingIds', 'setSelectedSurvivalLoadingIds', []),
- withState('survivalPlotLoading', 'setSurvivalPlotLoading', true),
- withProps(({ variable, data, fieldName }) => ({
- rawQueryData:
- variable.plotTypes === 'continuous'
- ? (
- (data.explore &&
- data.explore.cases.aggregations[fieldName.replace('.', '__')]
- .histogram) || {
- buckets: [],
- }
- ).buckets
- : (data || { buckets: [] }).buckets,
- totalDocs: (data.hits || { total: 0 }).total,
- })),
- withProps(
- ({
- variable, fieldName, setId, totalDocs,
- }) => ({
- getBucketRangesAndFilters: (acc, { doc_count, key }) => {
- const filters =
- variable.plotTypes === 'categorical'
- ? {}
- : {
- op: 'and',
- content: [
- {
- op: 'in',
- content: {
- field: 'cases.case_id',
- value: `set_id:${setId}`,
- },
- },
- {
- op: '>=',
- content: {
- field: fieldName,
- value: [`${valueIsYear(fieldName) ? Math.floor(key) : key}`],
- },
- },
- ...(acc.nextInterval !== 0 ? [
- {
- op: '<=',
- content: {
- field: fieldName,
- value: [`${acc.nextInterval - 1}`],
- },
- },
- ] : []),
- ],
- };
-
- return {
- nextInterval: key,
- data: [
- ...acc.data,
- {
- chart_doc_count: doc_count,
- doc_count: getCountLink({
- doc_count,
- filters,
- totalDocs,
- }),
- key: getRangeValue(key, fieldName, acc.nextInterval),
- rangeValues: {
- min: key,
- max: Math.floor(acc.nextInterval - 1),
- },
- filters,
- },
- ],
- };
- },
- })
- ),
- withState('selectedBuckets', 'setSelectedBuckets', []),
- withProps(
- ({
- setSurvivalPlotLoading,
- setSelectedSurvivalData,
- filters,
- fieldName,
- variable,
- setSelectedSurvivalValues,
- selectedSurvivalValues,
- setSelectedSurvivalLoadingIds,
- rawQueryData,
- getBucketRangesAndFilters,
- }) => ({
- populateSurvivalData: () => {
- setSurvivalPlotLoading(true);
-
- const dataForSurvival =
- variable.plotTypes === 'continuous'
- ? rawQueryData
- .sort((a, b) => b.key - a.key)
- .reduce(getBucketRangesAndFilters, {
- nextInterval: 0,
- data: [],
- })
- .data.slice(0)
- .reverse()
- : rawQueryData
- .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing'))
- .map(b => ({
- ...b,
- chart_doc_count: b.doc_count,
- }));
-
- const filteredData = dataForSurvival
- .filter(x => x.chart_doc_count >= MINIMUM_CASES)
- .filter(x => x.key !== '_missing');
-
- const continuousTop2Values =
- variable.plotTypes === 'continuous'
- ? filteredData
- .sort((a, b) => b.chart_doc_count - a.chart_doc_count)
- .slice(0, 2)
- : [];
-
- const valuesForTable =
- variable.plotTypes === 'categorical'
- ? filteredData.map(d => d.key).slice(0, 2)
- : continuousTop2Values.map(d => d.key);
-
- const valuesForPlot =
- variable.plotTypes === 'categorical'
- ? [...valuesForTable]
- : continuousTop2Values;
-
- setSelectedSurvivalValues(valuesForTable);
- setSelectedSurvivalLoadingIds(valuesForTable);
-
- getSurvivalCurvesArray({
- field: fieldName,
- values: valuesForPlot,
- currentFilters: filters,
- plotType: variable.plotTypes,
- }).then(data => {
- setSelectedSurvivalData(data);
- setSurvivalPlotLoading(false);
- setSelectedSurvivalLoadingIds([]);
- });
- },
- updateSelectedSurvivalValues: (data, value) => {
- if (
- selectedSurvivalValues.indexOf(value.key) === -1 &&
- selectedSurvivalValues.length >= MAXIMUM_CURVES
- ) {
- return;
- }
- setSurvivalPlotLoading(true);
-
- const nextValues =
- selectedSurvivalValues.indexOf(value.key) === -1
- ? selectedSurvivalValues.concat(value.key)
- : selectedSurvivalValues.filter(s => s !== value.key);
-
- setSelectedSurvivalValues(nextValues);
- setSelectedSurvivalLoadingIds(nextValues);
-
- const valuesForPlot =
- variable.plotTypes === 'categorical'
- ? [...nextValues]
- : nextValues
- .map(v => data.filter(d => d.key === v)[0])
- .map(data => ({
- ...data,
- doc_count: undefined,
- }));
-
- getSurvivalCurvesArray({
- field: fieldName,
- values: valuesForPlot,
- currentFilters: filters,
- plotType: variable.plotTypes,
- }).then(data => {
- setSelectedSurvivalData(data);
- setSurvivalPlotLoading(false);
- setSelectedSurvivalLoadingIds([]);
- });
- },
- })
- ),
- withPropsOnChange(
- (props, nextProps) => props.variable.active_chart !== nextProps.variable.active_chart ||
- !isEqual(props.data, nextProps.data),
- ({ populateSurvivalData, variable }) => {
- if (variable.active_chart === 'survival') {
- populateSurvivalData();
- }
- }
- ),
- withPropsOnChange(['id'], ({ setSelectedBuckets }) => setSelectedBuckets([])),
- lifecycle({
- componentDidMount(): void {
- const {
- dispatch, variable, fieldName, id,
- } = this.props;
- if (variable.scrollToCard === false) return;
- const offset = document.getElementById('header').getBoundingClientRect().bottom + 10;
- const wrapperId = makeWrapperId(fieldName);
- const $anchor = document.getElementById(wrapperId);
- const offsetTop = $anchor.getBoundingClientRect().top + window.pageYOffset;
- window.scroll({
- top: offsetTop - offset,
- behavior: 'smooth',
- });
- dispatch(
- updateClinicalAnalysisVariable({
- fieldName,
- variableKey: 'scrollToCard',
- value: false,
- id,
- })
- );
- },
- })
-)(ClinicalVariableCard);
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/helpers.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/helpers.js
new file mode 100644
index 000000000..fc198bc45
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/helpers.js
@@ -0,0 +1,52 @@
+export const dataDimensions = {
+ age_at_diagnosis: {
+ axisTitle: 'Age',
+ unit: 'Years',
+ },
+ cigarettes_per_day: {},
+ circumferential_resection_margin: {},
+ days_to_birth: { unit: 'Days' },
+ days_to_death: { unit: 'Days' },
+ days_to_hiv_diagnosis: { unit: 'Days' },
+ days_to_last_follow_up: { unit: 'Days' },
+ days_to_last_known_disease_status: { unit: 'Days' },
+ days_to_new_event: { unit: 'Days' },
+ days_to_recurrence: { unit: 'Days' },
+ days_to_treatment_end: { unit: 'Days' },
+ days_to_treatment_start: { unit: 'Days' },
+ height: {
+ axisTitle: 'Height',
+ unit: 'cm',
+ },
+ // ldh_level_at_diagnosis
+ // ldh_normal_range_upper
+ lymph_nodes_positive: {},
+ pack_years_smoked: {},
+ tumor_largest_dimension_diameter: {
+ axisTitle: 'Diameter',
+ unit: 'cm',
+ },
+ weight: {
+ axisTitle: 'Weight',
+ unit: 'kg',
+ },
+ year_of_diagnosis: { unit: 'Years' },
+ years_smoked: { unit: 'Years' },
+};
+
+// TODO the following table config warrants isolating a custom component
+
+export const boxTableAllowedStats = [
+ 'min',
+ 'max',
+ 'mean',
+ 'median',
+ 'sd',
+ 'iqr',
+];
+
+export const boxTableRenamedStats = {
+ Max: 'Maximum',
+ Min: 'Minimum',
+ SD: 'Standard Deviation',
+};
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js
new file mode 100644
index 000000000..dd7d5136e
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js
@@ -0,0 +1,1584 @@
+import React from 'react';
+import {
+ compose,
+ lifecycle,
+ setDisplayName,
+ withProps,
+ withPropsOnChange,
+ withState,
+} from 'recompose';
+import DownCaretIcon from 'react-icons/lib/fa/caret-down';
+import { connect } from 'react-redux';
+import {
+ find,
+ isEmpty,
+ isEqual,
+ min,
+ map,
+ max,
+ reject,
+ sortBy,
+ truncate,
+ groupBy,
+ get,
+ reduce,
+} from 'lodash';
+import { scaleOrdinal, schemeCategory10 } from 'd3';
+
+import { Row, Column } from '@ncigdc/uikit/Flex';
+import Button from '@ncigdc/uikit/Button';
+import { Tooltip, TooltipInjector } from '@ncigdc/uikit/Tooltip';
+import { visualizingButton, zDepth1 } from '@ncigdc/theme/mixins';
+
+import EntityPageHorizontalTable from '@ncigdc/components/EntityPageHorizontalTable';
+import Dropdown from '@ncigdc/uikit/Dropdown';
+import DropdownItem from '@ncigdc/uikit/DropdownItem';
+import Hidden from '@ncigdc/components/Hidden';
+import BarChart from '@ncigdc/components/Charts/BarChart';
+import ExploreLink from '@ncigdc/components/Links/ExploreLink';
+import { makeFilter, addInFilters } from '@ncigdc/utils/filters';
+import { CreateExploreCaseSetButton, AppendExploreCaseSetButton, RemoveFromExploreCaseSetButton } from '@ncigdc/modern_components/withSetAction';
+
+import { setModal } from '@ncigdc/dux/modal';
+import SaveSetModal from '@ncigdc/components/Modals/SaveSetModal';
+import AppendSetModal from '@ncigdc/components/Modals/AppendSetModal';
+import RemoveSetModal from '@ncigdc/components/Modals/RemoveSetModal';
+import GroupValuesModal from '@ncigdc/components/Modals/GroupValuesModal';
+import DownloadVisualizationButton from '@ncigdc/components/DownloadVisualizationButton';
+import wrapSvg from '@ncigdc/utils/wrapSvg';
+import {
+ DAYS_IN_YEAR,
+} from '@ncigdc/utils/ageDisplay';
+import { downloadToTSV } from '@ncigdc/components/DownloadTableToTsvButton';
+import QQPlotQuery from '@ncigdc/modern_components/QQPlot/QQPlotQuery';
+import BoxPlotWrapper from '@oncojs/boxplot';
+
+// survival plot
+import SurvivalPlotWrapper from '@ncigdc/components/SurvivalPlotWrapper';
+import {
+ getSurvivalCurvesArray,
+ MAXIMUM_CURVES,
+ MINIMUM_CASES,
+} from '@ncigdc/utils/survivalplot';
+import '../survivalPlot.css';
+import {
+ SpinnerIcon,
+ CloseIcon,
+ SurvivalIcon,
+ BarChartIcon,
+ BoxPlot,
+} from '@ncigdc/theme/icons';
+import { withTheme } from '@ncigdc/theme';
+
+import {
+ removeClinicalAnalysisVariable,
+ updateClinicalAnalysisVariable,
+} from '@ncigdc/dux/analysis';
+import { humanify } from '@ncigdc/utils/string';
+import timestamp from '@ncigdc/utils/timestamp';
+
+import { IS_CDAVE_DEV, analysisColors } from '@ncigdc/utils/constants';
+import {
+ boxTableAllowedStats,
+ boxTableRenamedStats,
+ dataDimensions,
+} from './helpers';
+
+import '../boxplot.css';
+import '../qq.css';
+
+const colors = scaleOrdinal(schemeCategory10);
+
+interface ITableHeading {
+ key: string;
+ title: string;
+ style?: React.CSSProperties;
+}
+
+type TPlotType = 'categorical' | 'continuous';
+type TActiveChart = 'box' | 'survival' | 'histogram';
+type TActiveCalculation = 'number' | 'percentage';
+type TVariableType =
+ | 'Demographic'
+ | 'Diagnosis'
+ | 'Exposure'
+ | 'Treatment'
+ | 'Follow_up' // confirm type name
+ | 'Molecular_test'; // confirm type name
+
+interface IVariable {
+ bins: any[]; // tbd - bins still need spec
+ active_calculation: TActiveCalculation;
+ active_chart: TActiveChart;
+ plotTypes: TPlotType;
+ type: TVariableType;
+}
+
+interface IVariableCardProps {
+ variable: IVariable;
+ fieldName: string;
+ plots: any[];
+ style: React.CSSProperties;
+ theme: IThemeProps;
+ dispatch: (arg: any) => void;
+ id: string;
+ survivalData: any[];
+}
+
+interface IVizButton {
+ title: string;
+ icon: JSX.Element;
+ action: (
+ payload: IAnalysisPayload
+ ) => { type: string, payload: IAnalysisPayload };
+}
+
+interface IVizButtons {
+ survival: IVizButton;
+ histogram: IVizButton;
+ box: IVizButton;
+ delete: IVizButton;
+}
+
+const CHART_HEIGHT = 250;
+const QQ_PLOT_RATIO = '70%';
+const BOX_PLOT_RATIO = '30%';
+
+const vizButtons: IVizButtons = {
+ box: {
+ action: updateClinicalAnalysisVariable,
+ icon: (
+ ),
+ title: 'Box/QQ Plot',
+ },
+ delete: {
+ action: removeClinicalAnalysisVariable,
+ icon: (
+ ),
+ title: 'Remove Card',
+ },
+ histogram: {
+ action: updateClinicalAnalysisVariable,
+ icon: (
+ ),
+ title: 'Histogram',
+ },
+ survival: {
+ action: updateClinicalAnalysisVariable,
+ icon: ,
+ title: 'Survival Plot',
+ },
+};
+
+const styles = {
+ actionMenuItem: {
+ cursor: 'pointer',
+ lineHeight: '1.5',
+ },
+ actionMenuItemDisabled: (theme: IThemeProps) => ({
+ ':hover': {
+ backgroundColor: 'transparent',
+ color: theme.greyScale5,
+ cursor: 'not-allowed',
+ },
+ color: theme.greyScale5,
+ cursor: 'not-allowed',
+ }),
+ activeButton: (theme: IThemeProps) => ({
+ ...styles.common(theme),
+ backgroundColor: theme.primary,
+ border: `1px solid ${theme.primary}`,
+ color: '#fff',
+ }),
+ common: (theme: IThemeProps) => ({
+ ':hover': {
+ backgroundColor: 'rgb(0,138,224)',
+ border: '1px solid rgb(0,138,224)',
+ color: '#fff',
+ },
+ backgroundColor: 'transparent',
+ border: `1px solid ${theme.greyScale4}`,
+ color: theme.greyScale2,
+ justifyContent: 'flex-start',
+ }),
+ histogram: (theme: IThemeProps) => ({
+ axis: {
+ fontSize: '0.9rem',
+ fontWeight: '500',
+ stroke: theme.greyScale4,
+ textFill: theme.greyScale3,
+ },
+ }),
+};
+
+const parseBucketValue = value => (value % 1
+ ? Number.parseFloat(value).toFixed(2)
+ : Math.round(value * 100) / 100);
+
+const getRangeValue = (key, nextInterval) => `${parseBucketValue(key)}${
+ nextInterval === 0
+ ? ' and up'
+ : ` to ${parseBucketValue(nextInterval - 1)}`
+ }`;
+
+const getCountLink = ({ doc_count, filters, totalDocs }) => (
+
+
+ {(doc_count || 0).toLocaleString()}
+
+ {` (${(((doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`}
+
+);
+
+const getCategoricalSetFilters = (selectedBuckets, fieldName, filters) => {
+ const bucketFilters = []
+ .concat(selectedBuckets.filter(bucket => bucket.key !== '_missing').length > 0 && [
+ {
+ content: {
+ field: fieldName,
+ value: selectedBuckets
+ .filter(bucket => bucket.key !== '_missing')
+ .reduce((acc, selectedBucket) => [...acc, ...selectedBucket.keyArray], []),
+ },
+ op: 'in',
+ },
+ ])
+ .concat(selectedBuckets.some(bucket => bucket.key === '_missing') && [
+ {
+ content: {
+ field: fieldName,
+ value: 'MISSING',
+ },
+ op: 'is',
+ },
+ ])
+ .filter(item => item);
+
+ return Object.assign(
+ {},
+ filters,
+ bucketFilters.length && {
+ content: filters.content
+ .concat(
+ bucketFilters.length > 1
+ ? {
+ content: bucketFilters,
+ op: 'or',
+ }
+ : bucketFilters[0]
+ ),
+ }
+ );
+};
+
+const getContinuousSetFilters = (selectedBuckets, fieldName, filters) => {
+ const bucketRanges = selectedBuckets.map(b => b.rangeValues);
+ if (bucketRanges.length === 1 && bucketRanges[0].max === -1) {
+ return addInFilters(filters, {
+ content: [
+ {
+ content: {
+ field: fieldName,
+ value: [bucketRanges[0].min],
+ },
+ op: '>=',
+ },
+ ],
+ op: 'and',
+ });
+ }
+ return addInFilters(filters, {
+ content: [
+ {
+ content: {
+ field: fieldName,
+ value: [min(bucketRanges.map(range => range.min))],
+ },
+ op: '>=',
+ },
+ {
+ content: {
+ field: fieldName,
+ value: [max(bucketRanges.map(range => range.max))],
+ },
+ op: '<=',
+ },
+ ],
+ op: 'and',
+ });
+};
+
+const getCardFilters = (variablePlotTypes, selectedBuckets, fieldName, filters) => {
+ return (get(selectedBuckets, 'length', 0)
+ ? variablePlotTypes === 'continuous'
+ ? getContinuousSetFilters(selectedBuckets, fieldName, filters)
+ : getCategoricalSetFilters(selectedBuckets, fieldName, filters)
+ : filters);
+};
+
+const getCategoricalTableData = (
+ binData,
+ getBucketRangesAndFilters,
+ fieldName,
+ totalDocs,
+ selectedSurvivalValues,
+ setId,
+ selectedBuckets,
+ setSelectedBuckets,
+ variable,
+ updateSelectedSurvivalValues,
+ selectedSurvivalLoadingIds,
+) => {
+ if (isEmpty(binData)) {
+ return [];
+ }
+ const displayData = variable.plotTypes === 'continuous'
+ ? binData
+ .sort((a, b) => b.key - a.key)
+ .reduce(getBucketRangesAndFilters, {
+ data: [],
+ nextInterval: 0,
+ })
+ .data.slice(0)
+ .reverse()
+ : binData
+ .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing'))
+ .sort((a, b) => b.doc_count - a.doc_count)
+ .map(b => ({
+ ...b,
+ chart_doc_count: b.doc_count,
+ doc_count: getCountLink({
+ doc_count: b.doc_count,
+ filters:
+ b.key === '_missing'
+ ? {
+ content: [
+ {
+ content: {
+ field: fieldName,
+ value: [...b.keyArray],
+ },
+ op: 'IS',
+ },
+ {
+ content: {
+ field: 'cases.case_id',
+ value: `set_id:${setId}`,
+ },
+ op: 'in',
+ },
+ ],
+ op: 'AND',
+ }
+ : makeFilter([
+ {
+ field: 'cases.case_id',
+ value: `set_id:${setId}`,
+ },
+ {
+ field: fieldName,
+ value: [...b.keyArray],
+ },
+ ]),
+ totalDocs,
+ }),
+ key: b.key,
+ }));
+
+ return displayData.map(b => ({
+ ...b,
+ select: (
+ {
+ if (find(selectedBuckets, { key: b.key })) {
+ setSelectedBuckets(
+ reject(selectedBuckets, r => r.key === b.key)
+ );
+ } else {
+ setSelectedBuckets([...selectedBuckets, b]);
+ }
+ }}
+ style={{
+ marginLeft: 3,
+ pointerEvents: 'initial',
+ }}
+ type="checkbox"
+ value={b.key}
+ />
+ ),
+ ...(variable.active_chart === 'survival'
+ ? {
+ survival: (
+ -1
+ ? `Click icon to remove ${b.key}`
+ : selectedSurvivalValues.length < MAXIMUM_CURVES
+ ? `Click icon to plot ${b.key}`
+ : `Maximum plots (${MAXIMUM_CURVES}) reached`
+ }
+ >
+ = MAXIMUM_CURVES &&
+ selectedSurvivalValues.indexOf(b.key) === -1)
+ }
+ onClick={() => {
+ updateSelectedSurvivalValues(displayData, b);
+ }}
+ style={{
+ backgroundColor:
+ selectedSurvivalValues.indexOf(b.key) === -1
+ ? '#666'
+ : colors(selectedSurvivalValues.indexOf(b.key)),
+ color: 'white',
+ margin: '0 auto',
+ opacity:
+ b.key === '_missing' ||
+ b.chart_doc_count < MINIMUM_CASES ||
+ (selectedSurvivalValues.length >= MAXIMUM_CURVES &&
+ selectedSurvivalValues.indexOf(b.key) === -1)
+ ? '0.33'
+ : '1',
+ padding: '2px 3px',
+ position: 'static',
+ }}
+ >
+ {selectedSurvivalLoadingIds.indexOf(b.key) !== -1 ? (
+
+ ) : (
+
+ )}
+
+
+ ),
+ }
+ : {}),
+ }));
+};
+
+const getBoxTableData = (data = {}) => (
+ Object.keys(data).length
+ ? sortBy(Object.keys(data), datum => boxTableAllowedStats.indexOf(datum.toLowerCase()))
+ .reduce(
+ (tableData, stat) => (
+ boxTableAllowedStats.includes(stat.toLowerCase())
+ ? tableData.concat({
+ count: data[stat],
+ stat: boxTableRenamedStats[stat] || stat, // Shows the descriptive label
+ })
+ : tableData
+ ), []
+ )
+ : []
+);
+
+const getHeadings = (chartType, dataDimension, fieldName) => {
+ return chartType === 'box'
+ ? [
+ {
+ key: 'stat',
+ title: 'Statistics',
+ },
+ {
+ key: 'count',
+ style: { textAlign: 'right' },
+ title: `${dataDimension || 'Quantities'}`,
+ },
+ ]
+ : [
+ {
+ key: 'select',
+ thStyle: {
+ position: 'sticky',
+ top: 0,
+ },
+ title: 'Select',
+ },
+ {
+ key: 'key',
+ thStyle: {
+ position: 'sticky',
+ top: 0,
+ },
+ title: humanify({ term: fieldName }),
+ },
+ {
+ key: 'doc_count',
+ style: { textAlign: 'right' },
+ thStyle: {
+ position: 'sticky',
+ textAlign: 'right',
+ top: 0,
+ },
+ title: '# Cases',
+ },
+ ...(chartType === 'survival' ? [
+ {
+ key: 'survival',
+ style: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ },
+ thStyle: {
+ position: 'sticky',
+ textAlign: 'right',
+ top: 0,
+ },
+ title: 'Survival',
+ },
+ ] : []),
+ ];
+};
+
+const ClinicalVariableCard: React.ComponentType = ({
+ currentAnalysis,
+ dataBuckets,
+ customBins,
+ dataDimension,
+ dataValues,
+ dispatch,
+ fieldName,
+ filters,
+ getBucketRangesAndFilters,
+ id,
+ overallSurvivalData,
+ plots,
+ selectedBuckets,
+ selectedSurvivalData,
+ selectedSurvivalLoadingIds,
+ selectedSurvivalValues,
+ setId,
+ setSelectedBuckets,
+ style = {},
+ survivalPlotLoading,
+ theme,
+ totalDocs,
+ updateSelectedSurvivalValues,
+ variable,
+ wrapperId,
+ qqData,
+ setQQData,
+ setQQDataIsSet,
+}) => {
+ const tableData = variable.active_chart === 'box'
+ ? getBoxTableData(dataValues)
+ : getCategoricalTableData(
+ customBins,
+ getBucketRangesAndFilters,
+ fieldName,
+ totalDocs,
+ selectedSurvivalValues,
+ setId,
+ selectedBuckets,
+ setSelectedBuckets,
+ variable,
+ updateSelectedSurvivalValues,
+ selectedSurvivalLoadingIds,
+ );
+
+ const chartData =
+ variable.active_chart === 'histogram'
+ ? tableData.map(d => {
+ return {
+ fullLabel: d.key,
+ label: truncate(d.key, { length: 18 }),
+ tooltip: `${d.key}: ${d.chart_doc_count.toLocaleString()}`,
+ value:
+ variable.active_calculation === 'number'
+ ? d.chart_doc_count
+ : (d.chart_doc_count / totalDocs) * 100,
+ };
+ })
+ : [];
+
+ // set action will default to cohort total when no buckets are selected
+ const totalFromSelectedBuckets = selectedBuckets && selectedBuckets.length
+ ? selectedBuckets.reduce((acc, b) => acc + b.chart_doc_count, 0)
+ : totalDocs;
+
+ const tsvSubstring = fieldName.replace(/\./g, '-');
+ const cardFilters = getCardFilters(variable.plotTypes, selectedBuckets, fieldName, filters);
+ const setActionsDisabled = get(selectedBuckets, 'length', 0) === 0;
+ return (
+
+
+
+ {humanify({ term: fieldName })}
+
+
+ {plots.concat('delete')
+ .map(plotType => (
+
+ {
+ dispatch(
+ vizButtons[plotType].action({
+ fieldName,
+ id,
+ value: plotType,
+ variableKey: 'active_chart',
+ })
+ );
+ }}
+ style={{
+ ...(plotType === variable.active_chart
+ ? styles.activeButton(theme)
+ : styles.common(theme)),
+ margin: 2,
+ }}
+ >
+ {vizButtons[plotType].title}
+ {vizButtons[plotType].icon}
+
+
+ ))}
+
+
+ {isEmpty(tableData)
+ ? (
+
+ There is no data for this facet
+
+ )
+ : (
+
+ {['histogram'].includes(variable.active_chart) && (
+
+
+ {/* {variable.active_chart === 'survival' && (
+
+
+
+ Log Rank Test P=Value ={' '}
+
+
+ )} */}
+
+ )}
+
+ {variable.active_chart === 'histogram' && (
+
+ )}
+ {variable.active_chart === 'survival' && (
+
+ {selectedSurvivalValues.length === 0 ? (
+
+ ) : (
+
+ )}
+
+ )}
+ {variable.active_chart === 'box' && (
+
+
+
+
+ Box Plot
+
+
+
+ wrapSvg({
+ className: 'boxplot',
+ selector: `#${wrapperId}-boxplot-container figure svg`,
+ title: `${humanify({ term: fieldName })} Box Plot`,
+ })
+ }
+ tooltipHTML="Download SVG or PNG"
+ />
+
+
+
+ QQ Plot
+
+
+
+ wrapSvg({
+ className: 'qq-plot',
+ selector: `#${wrapperId}-qqplot-container .qq-plot svg`,
+ title: `${humanify({ term: fieldName })} QQ Plot`,
+ })}
+ tooltipHTML="Download plot data"
+ tsvData={qqData}
+ />
+
+
+
+
+
+
+
+
+
+ setQQData(data)}
+ fieldName={fieldName}
+ filters={cardFilters}
+ first={totalDocs}
+ setDataHandler={() => setQQDataIsSet()}
+ setId={setId}
+ wrapperId={wrapperId}
+ />
+
+
+
+ )}
+
+
+
+ )}
+ {!isEmpty(tableData) && (
+
+
+ }
+ style={{
+ ...visualizingButton,
+ padding: '0 12px',
+ }}
+ >
+ Select action
+
+ )}
+ dropdownStyle={{
+ left: 0,
+ minWidth: 205,
+ }}
+ >
+ {[
+ ...(variable.active_chart !== 'box' ? [
+
+ {
+ if (setActionsDisabled) {
+ return;
+ }
+ dispatch(
+ setModal(
+
+ )
+ );
+ }}
+ >
+ Save as new case set
+
+ ,
+
+ {
+ if (setActionsDisabled) {
+ return;
+ }
+ dispatch(
+ setModal(
+
+ )
+ );
+ }}
+ >
+ Add to existing case set
+
+ ,
+
+ {
+ if (setActionsDisabled) { return; }
+ dispatch(
+ setModal(
+
+ )
+ );
+ }}
+ >
+ Remove from existing case set
+
+ ,
+ ] : []),
+ downloadToTSV({
+ filename: `analysis-${
+ currentAnalysis.name
+ }-${tsvSubstring}.${timestamp()}.tsv`,
+ selector: `#analysis-${tsvSubstring}-table`,
+ })
+ }
+ style={{
+ ...styles.actionMenuItem,
+ borderTop: variable.active_chart !== 'box' ? `1px solid ${theme.greyScale5}` : '',
+ }}
+ >
+ Export TSV
+ ,
+ ]}
+
+ {
+ variable.active_chart !== 'box' && (
+ }
+ style={{
+ ...visualizingButton,
+ padding: '0 12px',
+ }}
+ >
+ Customize Bins
+
+ )}
+ dropdownStyle={{ right: 0 }}
+ >
+ dispatch(
+ setModal(
+ dispatch(setModal(null))}
+ onUpdate={(newBins) => {
+ dispatch(
+ updateClinicalAnalysisVariable({
+ fieldName,
+ id,
+ value: newBins,
+ variableKey: 'bins',
+ }),
+ );
+ dispatch(setModal(null));
+ }
+ }
+ />,
+ ),
+ )
+ }
+ style={styles.actionMenuItem}
+ >
+ Edit Bins
+
+ {
+ dispatch(
+ updateClinicalAnalysisVariable({
+ fieldName,
+ id,
+ value: dataBuckets.reduce((acc, r) => ({
+ ...acc,
+ [r.key]: {
+ ...r,
+ groupName: r.key,
+ },
+ }), {}),
+ variableKey: 'bins',
+ }),
+ );
+ }}
+ style={styles.actionMenuItem}
+ >
+ Reset to Default
+
+
+ )
+ }
+
+
+
+ )}
+
+ );
+};
+
+export default compose(
+ setDisplayName('EnhancedClinicalVariableCard'),
+ connect((state: any) => ({ analysis: state.analysis })),
+ withTheme,
+ withState('selectedSurvivalData', 'setSelectedSurvivalData', {}),
+ withState('selectedSurvivalValues', 'setSelectedSurvivalValues', []),
+ withState('selectedSurvivalLoadingIds', 'setSelectedSurvivalLoadingIds', []),
+ withState('survivalPlotLoading', 'setSurvivalPlotLoading', true),
+ withState('selectedBuckets', 'setSelectedBuckets', []),
+ withState('qqData', 'setQQData', []),
+ withState('qqDataIsSet', 'setQQDataIsSet', false),
+ withProps(({ data, fieldName, variable }) => {
+ const sanitisedId = fieldName.split('.').pop();
+ const rawQueryData = get(data, `explore.cases.aggregations.${fieldName.replace('.', '__')}`, data);
+ const dataDimension = dataDimensions[sanitisedId] && dataDimensions[sanitisedId].unit;
+ return Object.assign(
+ {
+ dataBuckets: get(rawQueryData, variable.plotTypes === 'continuous' ? 'range.buckets' : 'buckets', []),
+ dataValues: variable.plotTypes === 'continuous' && map(
+ {
+ ...rawQueryData.stats,
+ ...rawQueryData.percentiles,
+ },
+ (value, stat) => {
+ switch (dataDimension) {
+ case 'Year': {
+ const age = Number.parseFloat(value / DAYS_IN_YEAR).toFixed(2);
+ return ({
+ [stat]: age % 1 ? age : Number.parseFloat(age).toFixed(0),
+ });
+ }
+ default:
+ return ({
+ [stat]: value % 1 ? Number.parseFloat(value).toFixed(2) : value,
+ });
+ }
+ }
+ ).reduce((acc, item) => ({
+ ...acc,
+ ...item,
+ }), {}),
+ totalDocs: get(data, 'hits.total', 0),
+ wrapperId: `${sanitisedId}-chart`,
+ },
+ dataDimensions[sanitisedId] && {
+ axisTitle: dataDimensions[sanitisedId].axisTitle,
+ dataDimension: dataDimensions[sanitisedId].unit,
+ dataValues:
+ map(
+ {
+ ...rawQueryData.stats,
+ ...rawQueryData.percentiles,
+ },
+ (value, stat) => {
+ switch (dataDimensions[sanitisedId].unit) {
+ case 'Years': {
+ const age = Number.parseFloat(value / DAYS_IN_YEAR).toFixed(2);
+ return ({
+ [stat]: age % 1 ? age : Number.parseFloat(age).toFixed(0),
+ });
+ }
+ default:
+ return ({
+ [stat]: value % 1 ? Number.parseFloat(value).toFixed(2) : value,
+ });
+ }
+ }
+ ).reduce((acc, item) => ({
+ ...acc,
+ ...item,
+ }), {}),
+ },
+ );
+ }),
+ withPropsOnChange(
+ (props, nextProps) => !isEqual(props.dataBuckets, nextProps.dataBuckets) || props.setId !== nextProps.setId,
+ ({
+ dataBuckets,
+ dispatch,
+ fieldName,
+ id,
+ variable,
+ }) => {
+ dispatch(
+ updateClinicalAnalysisVariable({
+ fieldName,
+ id,
+ value: {
+ ...reduce(variable.bins, (acc, bin, key) => {
+ if (bin.groupName && bin.groupName !== key) {
+ return {
+ ...acc,
+ [key]: {
+ doc_count: 0,
+ groupName: bin.groupName,
+ key,
+ },
+ };
+ }
+ return acc;
+ }, {}),
+ ...dataBuckets.reduce((acc, r) => ({
+ ...acc,
+ [r.key]: {
+ ...r,
+ groupName:
+ typeof get(variable, `bins.${r.key}.groupName`, undefined) === 'string' // hidden value have groupName '', so check if it is string
+ ? get(variable, `bins.${r.key}.groupName`, undefined)
+ : r.key,
+ },
+ }), {}),
+ },
+ variableKey: 'bins',
+ }),
+ );
+ }
+ ),
+ withProps(
+ ({
+ fieldName,
+ setId,
+ totalDocs,
+ variable,
+ }) => ({
+ customBins: map(groupBy(variable.bins, bin => bin.groupName), (values, key) => ({
+ doc_count: values.reduce((acc, value) => acc + value.doc_count, 0),
+ key,
+ keyArray: values.reduce((acc, value) => [...acc, value.key], []),
+ })).filter(bin => bin.key),
+ getBucketRangesAndFilters: (acc, { doc_count, key }) => {
+ const filters =
+ variable.plotTypes === 'categorical'
+ ? {}
+ : {
+ content: [
+ {
+ content: {
+ field: 'cases.case_id',
+ value: `set_id:${setId}`,
+ },
+ op: 'in',
+ },
+ {
+ content: {
+ field: fieldName,
+ value: [`${parseBucketValue(key)}`],
+ },
+ op: '>=',
+ },
+ ...(acc.nextInterval !== 0 ? [
+ {
+ content: {
+ field: fieldName,
+ value: [`${parseBucketValue(acc.nextInterval - 1)}`],
+ },
+ op: '<=',
+ },
+ ] : []),
+ ],
+ op: 'and',
+ };
+
+ return {
+ data: [
+ ...acc.data,
+ {
+ chart_doc_count: doc_count,
+ doc_count: getCountLink({
+ doc_count,
+ filters,
+ totalDocs,
+ }),
+ filters,
+ key: getRangeValue(key, acc.nextInterval),
+ rangeValues: {
+ max: Math.floor(acc.nextInterval - 1),
+ min: key,
+ },
+ },
+ ],
+ nextInterval: key,
+ };
+ },
+ })
+ ),
+ withProps(
+ ({
+ customBins,
+ dataBuckets,
+ fieldName,
+ filters,
+ getBucketRangesAndFilters,
+ selectedSurvivalValues,
+ setSelectedSurvivalData,
+ setSelectedSurvivalLoadingIds,
+ setSelectedSurvivalValues,
+ setSurvivalPlotLoading,
+ variable,
+ }) => ({
+ populateSurvivalData: () => {
+ setSurvivalPlotLoading(true);
+ const dataForSurvival =
+ variable.plotTypes === 'continuous'
+ ? dataBuckets
+ .sort((a, b) => b.key - a.key)
+ .reduce(getBucketRangesAndFilters, {
+ data: [],
+ nextInterval: 0,
+ })
+ .data.slice(0)
+ .reverse()
+ : customBins
+ .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing'))
+ .map(b => ({
+ ...b,
+ chart_doc_count: b.doc_count,
+ }));
+
+ const filteredData = dataForSurvival
+ .filter(x => x.chart_doc_count >= MINIMUM_CASES)
+ .filter(x => x.key !== '_missing');
+
+ const continuousTop2Values =
+ variable.plotTypes === 'continuous'
+ ? filteredData
+ .sort((a, b) => b.chart_doc_count - a.chart_doc_count)
+ .slice(0, 2)
+ : [];
+
+ const valuesForTable =
+ variable.plotTypes === 'categorical'
+ ? filteredData.map(d => d.key).slice(0, 2)
+ : continuousTop2Values.map(d => d.key);
+
+ const valuesForPlot =
+ variable.plotTypes === 'categorical'
+ ? [...valuesForTable]
+ : continuousTop2Values;
+
+ setSelectedSurvivalValues(valuesForTable);
+ setSelectedSurvivalLoadingIds(valuesForTable);
+
+ getSurvivalCurvesArray({
+ currentFilters: filters,
+ field: fieldName,
+ plotType: variable.plotTypes,
+ values: valuesForPlot,
+ }).then(data => {
+ setSelectedSurvivalData(data);
+ setSurvivalPlotLoading(false);
+ setSelectedSurvivalLoadingIds([]);
+ });
+ },
+ updateSelectedSurvivalValues: (data, value) => {
+ if (
+ selectedSurvivalValues.indexOf(value.key) === -1 &&
+ selectedSurvivalValues.length >= MAXIMUM_CURVES
+ ) {
+ return;
+ }
+ setSurvivalPlotLoading(true);
+
+ const nextValues =
+ selectedSurvivalValues.indexOf(value.key) === -1
+ ? selectedSurvivalValues.concat(value.key)
+ : selectedSurvivalValues.filter(s => s !== value.key);
+
+ setSelectedSurvivalValues(nextValues);
+ setSelectedSurvivalLoadingIds(nextValues);
+
+ const valuesForPlot =
+ variable.plotTypes === 'categorical'
+ ? [...nextValues]
+ : nextValues
+ .map(v => data.filter(d => d.key === v)[0])
+ .map(filteredData => ({
+ ...filteredData,
+ doc_count: undefined,
+ }));
+
+ getSurvivalCurvesArray({
+ currentFilters: filters,
+ field: fieldName,
+ plotType: variable.plotTypes,
+ values: valuesForPlot,
+ }).then(receivedData => {
+ setSelectedSurvivalData(receivedData);
+ setSurvivalPlotLoading(false);
+ setSelectedSurvivalLoadingIds([]);
+ });
+ },
+ })
+ ),
+ withPropsOnChange(
+ (props, nextProps) => props.variable.active_chart !== nextProps.variable.active_chart ||
+ !isEqual(props.data, nextProps.data),
+ ({ populateSurvivalData, variable }) => {
+ if (variable.active_chart === 'survival') {
+ populateSurvivalData();
+ }
+ }
+ ),
+ withPropsOnChange(['id'], ({ setSelectedBuckets }) => setSelectedBuckets([])),
+ lifecycle({
+ componentDidMount(): void {
+ const {
+ dispatch,
+ fieldName,
+ id,
+ variable,
+ wrapperId,
+ } = this.props;
+ if (variable.scrollToCard === false) return;
+ const offset = document.getElementById('header').getBoundingClientRect().bottom + 10;
+ const $anchor = document.getElementById(`${wrapperId}-container`);
+ if ($anchor) {
+ const offsetTop = $anchor.getBoundingClientRect().top + window.pageYOffset;
+ window.scroll({
+ behavior: 'smooth',
+ top: offsetTop - offset,
+ });
+ }
+
+ dispatch(
+ updateClinicalAnalysisVariable({
+ fieldName,
+ id,
+ value: false,
+ variableKey: 'scrollToCard',
+ })
+ );
+ },
+ })
+)(ClinicalVariableCard);
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/CohortDropdown.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/CohortDropdown.js
index d94625eb9..f5da18b8d 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/CohortDropdown.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/CohortDropdown.js
@@ -10,6 +10,7 @@ import { visualizingButton } from '@ncigdc/theme/mixins';
import Tooltip from '@ncigdc/uikit/Tooltip/Tooltip';
import { updateClinicalAnalysisSet } from '@ncigdc/dux/analysis';
+
export default ({
sets,
currentAnalysis,
@@ -21,8 +22,9 @@ export default ({
if (setKey !== _.keys(currentAnalysis.sets.case)[0]) {
return (
{
dispatch(
updateClinicalAnalysisSet({
@@ -31,9 +33,7 @@ export default ({
setName: name,
})
);
- }}
- aria-label={`Switch selected set to ${name}`}
- >
+ }}>
{name}
);
@@ -43,39 +43,39 @@ export default ({
const setName = _.first(_.values(currentAnalysis.sets.case));
return (
16 ? setName : null
- }
- >
+ disabled ? disabledMessage : setName.length > 13 ? setName : null
+ }>
}
buttonContentStyle={{
width: '100%',
justifyContent: 'space-between',
}}
+ className="cohort-dropdown"
disabled={disabled}
- >
+ rightIcon={ }
+ style={{
+ ...visualizingButton,
+ padding: '0 4px 0 6px',
+ width: 145,
+ justifyContent: 'flex-start',
+ }}>
{_.truncate(setName, {
- length: 16,
+ length: 13,
})}
- }
- dropdownStyle={{ left: 0, cursor: 'pointer' }}
- >
+ )}
+ dropdownStyle={{
+ left: 0,
+ cursor: 'pointer',
+ }}
+ isDisabled={disabled}
+ style={{
+ justifyContent: 'flex-start',
+ }}>
{dropdownItems}
);
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ContinuousAggregationQuery.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ContinuousAggregationQuery.js
index 40b177664..26132a3f1 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ContinuousAggregationQuery.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ContinuousAggregationQuery.js
@@ -2,11 +2,8 @@ import React from 'react';
import {
compose,
withPropsOnChange,
- branch,
- renderComponent,
withProps,
withState,
- lifecycle,
} from 'recompose';
import md5 from 'blueimp-md5';
import urlJoin from 'url-join';
@@ -14,8 +11,7 @@ import _ from 'lodash';
import consoleDebug from '@ncigdc/utils/consoleDebug';
import { redirectToLogin } from '@ncigdc/utils/auth';
-import Loader, { withLoader } from '@ncigdc/uikit/Loaders/Loader';
-
+import Loader from '@ncigdc/uikit/Loaders/Loader';
import { API, IS_AUTH_PORTAL } from '@ncigdc/utils/constants';
import ClinicalVariableCard from './ClinicalVariableCard';
@@ -24,25 +20,58 @@ const simpleAggCache = {};
const pendingAggCache = {};
const DEFAULT_CONTINUOUS_BUCKETS = 4;
-const getContinuousAggs = ({ fieldName, stats, filters }) => {
+const getContinuousAggs = ({ fieldName, stats, filters, bins }) => {
// prevent query failing if interval will equal 0
if (_.isNull(stats.min) || _.isNull(stats.max)) {
return null;
}
- const interval = (stats.max - stats.min) / DEFAULT_CONTINUOUS_BUCKETS;
+
+ let rangeArr = _.reduce(bins, (acc, bin, key) => {
+ if (
+ !!bin &&
+ (typeof bin.from === 'number') &&
+ (typeof bin.to === 'number') &&
+ stats.min <= bin.from &&
+ bin.from < bin.to &&
+ bin.to <= stats.max
+ ) {
+ return [...acc, { from: bin.from, to: bin.to }];
+ }
+ return acc;
+ }, []);
+ const interval = Math.round((stats.max - stats.min) / DEFAULT_CONTINUOUS_BUCKETS);
+ if (rangeArr.length === 0) {
+ rangeArr = Array(DEFAULT_CONTINUOUS_BUCKETS).fill(1).map(
+ (val, key) => ({
+ from: key * interval + stats.min,
+ to: (key + 1) === DEFAULT_CONTINUOUS_BUCKETS ? stats.max : (stats.min + (key + 1) * interval - 1),
+ })
+ )
+ }
+
const queryFieldName = fieldName.replace('.', '__');
+ const filters2 = {
+ op: "range",
+ content: [
+ {
+ ranges: rangeArr,
+ }
+ ]
+ }
+ const aggregationFieldName = fieldName.replace(/\./g, '__');
const variables = {
filters,
+ filters2,
};
const componentName = 'ContinuousAggregationQuery';
const body = JSON.stringify({
- query: `query ${componentName}($filters: FiltersArgument) {
+ query: `query ${componentName}($filters: FiltersArgument, $filters2: FiltersArgument) {
viewer {
explore {
cases {
aggregations(filters: $filters) {
- ${queryFieldName} {
+ ${aggregationFieldName} {
stats {
Min : min
Max: max
@@ -55,10 +84,10 @@ const getContinuousAggs = ({ fieldName, stats, filters }) => {
q1: quartile_1
q3: quartile_3
}
- histogram(interval: ${interval}) {
+ range(ranges: $filters2) {
buckets {
- key
doc_count
+ key
}
}
}
@@ -138,7 +167,7 @@ const getContinuousAggs = ({ fieldName, stats, filters }) => {
}
} else {
consoleDebug(
- `Something went wrong in environment, but no error status: ${err}`
+ `Something went wrong in environment, but no error status: ${err}`
);
}
}));
@@ -154,11 +183,15 @@ export default compose(
filters,
setAggData,
setIsLoading,
+ variable,
+ hits,
}) => {
const res = await getContinuousAggs({
fieldName,
stats,
filters,
+ bins: variable.bins,
+ hits,
});
setAggData(res && res.data.viewer, () => setIsLoading(false));
},
@@ -170,6 +203,7 @@ export default compose(
if (isLoading) {
return ;
}
+
return (
+ />
);
});
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ControlPanelNode.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ControlPanelNode.js
index ffc5cf068..fade5196c 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ControlPanelNode.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ControlPanelNode.js
@@ -16,8 +16,8 @@ import './reactToggle.css';
import { humanify } from '@ncigdc/utils/string';
import { Row, Column } from '@ncigdc/uikit/Flex';
import CollapsibleList from '@ncigdc/uikit/CollapsibleList';
-import { theme } from '@ncigdc/theme/index';
-import { withTheme } from '@ncigdc/theme';
+import { theme, withTheme } from '@ncigdc/theme/index';
+
import styled from '@ncigdc/theme/styled';
import AngleIcon from '@ncigdc/theme/icons/AngleIcon';
import { Tooltip } from '@ncigdc/uikit/Tooltip';
@@ -45,9 +45,8 @@ const getPlotType = field => {
field.type.name === 'NumericAggregations'
) {
return 'continuous';
- } else {
- return 'categorical';
}
+ return 'categorical';
};
// will need to add other types as they become available
@@ -104,17 +103,21 @@ const ClinicalGrouping = compose(
top: 0,
zIndex: 99,
}}
- >
+ >
setCollapsed(!collapsed)}
- >
+ style={{
+ ...style,
+ margin: '10px 0',
+ cursor: 'pointer',
+ }}
+ >
+ />
{name}
@@ -125,12 +128,12 @@ const ClinicalGrouping = compose(
style={{
padding: '0 10px',
}}
- >
+ >
{fields.length > MAX_FIELDS_LENGTH && showingMore && (
setShowingMore(!showingMore)}
- >
+ >
{'Less...'}
@@ -146,7 +149,9 @@ const ClinicalGrouping = compose(
}))
.map(
(
- { fieldDescription, fieldName, type, fieldTitle, plotTypes },
+ {
+ fieldDescription, fieldName, fieldTitle, plotTypes, type,
+ },
i
) => {
const checked = Object.keys(
@@ -169,7 +174,7 @@ const ClinicalGrouping = compose(
marginBottom: descMatch ? '10px' : '0',
fontStyle: descMatch ? 'italic' : 'normal',
}}
- >
+ >
{descMatch
? internalHighlight(searchValue, fieldDescription, {
backgroundColor: '#FFFF00',
@@ -184,7 +189,7 @@ const ClinicalGrouping = compose(
display: 'inline-block',
marginRight: '50px',
}}
- >
+ >
{internalHighlight(searchValue, fieldTitle, {
backgroundColor: '#FFFF00',
})}
@@ -192,11 +197,11 @@ const ClinicalGrouping = compose(
);
const ToggleEl = () => (
{
if (!type.name) {
return null;
@@ -211,7 +216,7 @@ const ClinicalGrouping = compose(
})
);
}}
- />
+ />
);
return (
-
+ >
+
+ >
{descMatch ? (
-
+
+ {' '}
+
) : (
-
- }>
-
-
-
-
+
+ }>
+
+
+
+
)}
@@ -256,7 +267,7 @@ const ClinicalGrouping = compose(
setShowingMore(!showingMore)}
- >
+ >
{showingMore
? 'Less...'
: fields.length - MAX_VISIBLE_FACETS &&
@@ -277,7 +288,7 @@ export default compose(
withTheme,
withPropsOnChange(
(props, nextProps) => props.searchValue !== nextProps.searchValue,
- ({ searchValue, clinicalAnalysisFields }) => {
+ ({ clinicalAnalysisFields, searchValue }) => {
const filteredFields = clinicalAnalysisFields
.map(field => ({
...field,
@@ -304,14 +315,14 @@ export default compose(
)
)(
({
- theme,
+ analysis_id,
+ clinicalAnalysisFields,
currentAnalysis,
dispatch,
- clinicalAnalysisFields,
- usefulFacets,
- analysis_id,
- searchValue,
groupedByClinicalType,
+ searchValue,
+ theme,
+ usefulFacets,
}) => {
return (
@@ -319,10 +330,15 @@ export default compose(
style={{
padding: '5px 15px 15px',
}}
- >
+ >
- {(_.keys(usefulFacets) || []).length} of{' '}
- {(clinicalAnalysisFields || []).length} fields with values
+ {(_.keys(usefulFacets) || []).length}
+ {' '}
+of
+ {' '}
+ {(clinicalAnalysisFields || []).length}
+ {' '}
+fields with values
+ >
{clinicalTypeOrder.map(clinicalType => {
const fields = groupedByClinicalType[clinicalType] || [];
return (
+ style={styles.category(theme)}
+ />
);
})}
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/boxplot.css b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/boxplot.css
new file mode 100644
index 000000000..a1ffafdee
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/boxplot.css
@@ -0,0 +1,28 @@
+.boxplot .axis path,
+.boxplot .axis line,
+.boxplot .decadeBar path {
+ stroke: #ccc;
+ shape-rendering: crispEdges;
+}
+
+.boxplot g text {
+ fill: #888;
+}
+
+.boxplot .axis .tick text,
+.boxplot .axis text {
+ fill: #ccc;
+ font-size: 1rem;
+}
+
+.boxplot .box line {
+ stroke: #1784AC;
+}
+
+.boxplot .whisker path {
+ stroke: #333;
+}
+
+.boxplot .point {
+ color: #333;
+}
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/qq.css b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/qq.css
new file mode 100644
index 000000000..a60b8ab3c
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/qq.css
@@ -0,0 +1,15 @@
+.qq-plot .axis path,
+.qq-plot .axis line {
+ stroke: #e6dede;
+ shape-rendering: crispEdges;
+}
+
+.qq-plot .axis .tick text,
+.qq-plot .axis text {
+ fill: #888;
+ font-size: 1rem;
+}
+
+.qq-plot g text {
+ fill: #888;
+}
diff --git a/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.js b/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.js
index 7bd04524e..c579120ef 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.js
@@ -1,7 +1,13 @@
// @flow
import React from 'react';
-import { compose, withState, branch, renderComponent } from 'recompose';
+import {
+ branch,
+ compose,
+ renderComponent,
+ setDisplayName,
+ withState,
+} from 'recompose';
import { connect } from 'react-redux';
import Card from '@ncigdc/uikit/Card';
import Tabs from '@ncigdc/uikit/Tabs';
@@ -23,21 +29,22 @@ import { makeFilter } from '@ncigdc/utils/filters';
const styles = {
common: theme => ({
- backgroundColor: 'transparent',
- color: theme.greyScale2,
- justifyContent: 'flex-start',
':hover': {
backgroundColor: theme.greyScale6,
},
+ backgroundColor: 'transparent',
+ color: theme.greyScale2,
+ justifyContent: 'flex-start',
}),
downloadButton: theme => ({
...styles.common(theme),
- padding: '3px 5px',
border: `1px solid ${theme.greyScale4}`,
+ padding: '3px 5px',
}),
};
export default compose(
+ setDisplayName('EnhancedClinicalCard'),
branch(
({ viewer }) => !viewer.repository.cases.hits.edges[0],
renderComponent(() =>
No case found.
),
@@ -45,21 +52,21 @@ export default compose(
connect(state => state.cart),
withState('activeTab', 'setTab', 0),
withState('state', 'setState', {
- tsvDownloading: false,
jsonDownloading: false,
+ tsvDownloading: false,
}),
withTheme,
)(
({
+ active,
activeTab,
+ dropdownStyle,
+ requests,
+ setState,
setTab,
+ state,
theme,
viewer: { repository: { cases: { hits: { edges } } } },
- dropdownStyle,
- active,
- state,
- setState,
- requests,
}) => {
const {
case_id: caseId,
@@ -73,55 +80,87 @@ export default compose(
} = edges[0].node;
const familyHistory = familyHistories.map(x => x.node);
const caseFilter = makeFilter([
- { field: 'cases.case_id', value: [caseId] },
+ {
+ field: 'cases.case_id',
+ value: [caseId],
+ },
]);
return (
Clinical
+ tsvFilename={`clinical.case-${submitterId}-${projectId}.${timestamp()}.tar.gz`}
+ />
- }
- >
+ )}
+ >
setTab(() => i)}
tabs={[
Demographic
,
- Diagnoses / Treatments ({diagnoses.length})
+ Diagnoses / Treatments (
+ {diagnoses.length}
+)
,
- Family Histories ({familyHistory.length})
+ Family Histories (
+ {familyHistory.length}
+)
+
,
+
+Exposures (
+ {totalExposures}
+)
,
- Exposures ({totalExposures})
,
]}
- activeIndex={activeTab}
- >
+ >
{activeTab === 0 && (
{!!demographic.demographic_id && (
+ />
)}
{!demographic.demographic_id && (
No Demographic Found.
@@ -132,22 +171,17 @@ export default compose(
{!!diagnoses.length && (
1
- ? diagnoses.map(x => (
-
- {truncate(x.node.diagnosis_id, { length: 11 })}
-
- ))
- : []
- }
+ contentStyle={{ border: 'none' }}
tabContent={diagnoses.map(({ node: x }) => (
+ />
+ >
Treatments (
{x.treatments ? x.treatments.hits.edges.length : 0}
)
@@ -215,14 +264,7 @@ export default compose(
{x.treatments &&
!!x.treatments.hits.edges.length && (
UUID,
- Therapeutic Agents ,
- Treatment Intent Type ,
- Treatment or Therapy ,
- Days to Treatment Start ,
- ]}
- body={
+ body={(
{x.treatments.hits.edges.map(({ node }) => (
@@ -238,17 +280,33 @@ export default compose(
))}
- }
- />
- )}
+ )}
+ headings={[
+ UUID ,
+ Therapeutic Agents ,
+ Treatment Intent Type ,
+ Treatment or Therapy ,
+ Days to Treatment Start ,
+ ]}
+ />
+ )}
{(!x.treatments || !x.treatments.hits.edges.length) && (
-
+
No Treatments Found.
-
- )}
+
+ )}
))}
- />
+ tabs={
+ diagnoses.length > 1
+ ? diagnoses.map(x => (
+
+ {truncate(x.node.diagnosis_id, { length: 11 })}
+
+ ))
+ : []
+ }
+ />
)}
{!diagnoses.length && (
No Diagnoses Found.
@@ -259,22 +317,16 @@ export default compose(
{!!familyHistory.length && (
1
- ? familyHistory.map(x => (
-
- {truncate(x.family_history_id, { length: 11 })}
-
- ))
- : []
- }
+ contentStyle={{ border: 'none' }}
tabContent={familyHistory.map(x => (
+ />
))}
- />
+ tabs={
+ familyHistory.length > 1
+ ? familyHistory.map(x => (
+
+ {truncate(x.family_history_id, { length: 11 })}
+
+ ))
+ : []
+ }
+ />
)}
{!familyHistory.length && (
@@ -308,30 +372,49 @@ export default compose(
{!!totalExposures && (
1
- ? exposures.map(x => (
-
- {truncate(x.node.exposure_id, { length: 11 })}
-
- ))
- : []
- }
+ contentStyle={{ border: 'none' }}
tabContent={exposures.map(x => (
+ />
))}
- />
+ tabs={
+ exposures.length > 1
+ ? exposures.map(x => (
+
+ {truncate(x.node.exposure_id, { length: 11 })}
+
+ ))
+ : []
+ }
+ />
)}
{!totalExposures && (
No Exposures Found.
@@ -346,10 +429,8 @@ export default compose(
borderTop: `1px solid ${theme.greyScale5}`,
marginTop: '10px',
}}
- >
+ >
{
const fileData = {
@@ -364,7 +445,8 @@ export default compose(
)}
{f.node.access === 'controlled' && (
- )}{' '}
+ )}
+ {' '}
{f.node.file_name}
),
@@ -376,36 +458,47 @@ export default compose(
display: 'flex',
flexDirection: 'row',
}}
- >
+ >
+ />
),
};
})}
headings={[
- { key: 'file_name', title: 'Filename' },
- { key: 'data_format', title: 'Data format' },
+ {
+ key: 'file_name',
+ title: 'Filename',
+ },
+ {
+ key: 'data_format',
+ title: 'Data format',
+ },
{
key: 'file_size',
title: 'Size',
style: { textAlign: 'right' },
},
- { key: 'action', title: 'Action' },
+ {
+ key: 'action',
+ title: 'Action',
+ },
]}
- />
+ title="Clinical Supplement File"
+ titleStyle={{ fontSize: '1em' }}
+ />
)}
diff --git a/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.relay.js b/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.relay.js
index eae1b8192..4936e855b 100644
--- a/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.relay.js
+++ b/src/packages/@ncigdc/modern_components/ClinicalCard/ClinicalCard.relay.js
@@ -3,42 +3,55 @@
import React from 'react';
import { graphql } from 'react-relay';
import { makeFilter } from '@ncigdc/utils/filters';
-import { compose, withPropsOnChange, branch, renderComponent } from 'recompose';
+import {
+ branch,
+ compose,
+ renderComponent,
+ setDisplayName,
+ withPropsOnChange,
+} from 'recompose';
import Query from '@ncigdc/modern_components/Query';
-export default (Component: ReactClass<*>) =>
- compose(
- branch(
- ({ caseId }) => !caseId,
- renderComponent(() => (
-
-
caseId must be provided
-
- )),
- ),
- withPropsOnChange(['caseId'], ({ caseId }) => {
- return {
- variables: {
- filters: makeFilter([
- {
- field: 'cases.case_id',
- value: [caseId],
- },
- ]),
- fileFilters: makeFilter([
- { field: 'cases.case_id', value: [caseId] },
- { field: 'files.data_type', value: ['Clinical Supplement'] },
- ]),
- },
- };
- }),
- )((props: Object) => {
- return (
- ) => compose(
+ setDisplayName('EnhancedClinicalCard_Relay'),
+ branch(
+ ({ caseId }) => !caseId,
+ renderComponent(() => (
+
+
caseId
+ {' '}
+must be provided
+
+ )),
+ ),
+ withPropsOnChange(['caseId'], ({ caseId }) => {
+ return {
+ variables: {
+ fileFilters: makeFilter([
+ {
+ field: 'cases.case_id',
+ value: [caseId],
+ },
+ {
+ field: 'files.data_type',
+ value: ['Clinical Supplement'],
+ },
+ ]),
+ filters: makeFilter([
+ {
+ field: 'cases.case_id',
+ value: [caseId],
+ },
+ ]),
+ },
+ };
+ }),
+)((props: Object) => {
+ return (
+ ) =>
morphology
primary_diagnosis
prior_malignancy
+ synchronous_malignancy
progression_or_recurrence
site_of_resection_or_biopsy
tissue_or_organ_of_origin
@@ -161,6 +175,7 @@ export default (Component: ReactClass<*>) =>
}
}
`}
- />
- );
- });
+ variables={props.variables}
+ />
+ );
+});
diff --git a/src/packages/@ncigdc/modern_components/DownloadBiospecimenDropdown/DownloadBiospecimenDropdown.js b/src/packages/@ncigdc/modern_components/DownloadBiospecimenDropdown/DownloadBiospecimenDropdown.js
index d257e40ac..fcae68f27 100644
--- a/src/packages/@ncigdc/modern_components/DownloadBiospecimenDropdown/DownloadBiospecimenDropdown.js
+++ b/src/packages/@ncigdc/modern_components/DownloadBiospecimenDropdown/DownloadBiospecimenDropdown.js
@@ -20,7 +20,6 @@ const styles = {
minWidth: '100%',
width: '100%',
left: '1px',
- borderRadius: '5px',
},
dropdownButton: {
marginLeft: '0.2rem',
@@ -72,7 +71,10 @@ export default compose(
const biospecimenCount = viewer ? viewer[scope].cases.hits.total : null;
const buttonProps = {
className: 'data-download-biospecimen',
- style: { ...styles.dropdownButton, ...buttonStyles },
+ style: {
+ ...styles.dropdownButton,
+ ...buttonStyles,
+ },
leftIcon:
state.setCreating || state.jsonDownloading || state.tsvDownloading ? (
@@ -82,20 +84,26 @@ export default compose(
};
return (
setState({ ...state, setCreating: true })}
+ onClick={() => setState({
+ ...state,
+ setCreating: true,
+ })}
onComplete={(setId, size) => {
- setState({ ...state, setId, size, setCreating: false });
+ setState({
+ ...state,
+ setId,
+ size,
+ setCreating: false,
+ });
}}
- displaySpinnerOverlay={false}
- >
+ >
{state.setCreating ||
state.jsonDownloading ||
state.tsvDownloading
@@ -108,78 +116,56 @@ export default compose(
? 'Processing'
: inactiveText}
- )
+ )
}
- dropdownStyle={{ ...styles.dropdownContainer, ...dropdownStyles }}
- >
+ className="data-download-biospecimen"
+ disabled={state.setCreating}
+ dropdownStyle={{
+ ...styles.dropdownContainer,
+ ...dropdownStyles,
+ }}
+ >
{state.setCreating ? (
) : (
- setState(s => ({
- ...s,
- tsvDownloading: currentState,
- }))}
- active={state.tsvDownloading}
- size={biospecimenCount ? biospecimenCount : state.size}
+ className="data-download-biospecimen-tsv"
+ endpoint="/biospecimen_tar"
+ filename={tsvFilename}
filters={
shouldCreateSet
? {
- op: 'and',
- content: [
- {
- op: 'in',
- content: {
- field: 'cases.case_id',
- value: [`set_id:${state.setId}`],
- },
+ op: 'and',
+ content: [
+ {
+ op: 'in',
+ content: {
+ field: 'cases.case_id',
+ value: [`set_id:${state.setId}`],
},
- ],
- }
+ },
+ ],
+ }
: filters
}
- filename={tsvFilename}
- />
- setState(s => ({
+ ...s,
+ tsvDownloading: currentState,
+ }))}
+ size={biospecimenCount || state.size}
style={styles.button(theme)}
- endpoint="/cases"
+ />
+
- setState(s => ({
- ...s,
- jsonDownloading: currentState,
- }))}
- active={state.jsonDownloading}
- filters={
- shouldCreateSet
- ? {
- op: 'and',
- content: [
- {
- op: 'in',
- content: {
- field: 'cases.case_id',
- value: [`set_id:${state.setId}`],
- },
- },
- ],
- }
- : filters
- }
- size={biospecimenCount ? biospecimenCount : state.size}
- fields={['case_id']}
+ className="data-download-biospecimen"
dataExportExpands={[
'samples',
'samples.portions',
@@ -192,8 +178,33 @@ export default compose(
'samples.portions.annotations',
'samples.portions.center',
]}
+ endpoint="/cases"
+ fields={['case_id']}
filename={jsonFilename}
- />
+ filters={
+ shouldCreateSet
+ ? {
+ op: 'and',
+ content: [
+ {
+ op: 'in',
+ content: {
+ field: 'cases.case_id',
+ value: [`set_id:${state.setId}`],
+ },
+ },
+ ],
+ }
+ : filters
+ }
+ inactiveText="JSON"
+ setParentState={currentState => setState(s => ({
+ ...s,
+ jsonDownloading: currentState,
+ }))}
+ size={biospecimenCount || state.size}
+ style={styles.button(theme)}
+ />
)}
diff --git a/src/packages/@ncigdc/modern_components/DownloadClinicalDropdown/DownloadClinicalDropdown.js b/src/packages/@ncigdc/modern_components/DownloadClinicalDropdown/DownloadClinicalDropdown.js
index 25d90834d..b71452b8b 100644
--- a/src/packages/@ncigdc/modern_components/DownloadClinicalDropdown/DownloadClinicalDropdown.js
+++ b/src/packages/@ncigdc/modern_components/DownloadClinicalDropdown/DownloadClinicalDropdown.js
@@ -17,7 +17,6 @@ export const styles = {
minWidth: '100%',
width: '100%',
left: '1px',
- borderRadius: '5px',
},
dropdownButton: {
marginLeft: '0.2rem',
@@ -66,65 +65,56 @@ export default compose(
const clinicalCount = viewer ? viewer[scope].cases.hits.total : null;
return (
) : (
- )
+ )
}
- >
+ style={{
+ ...styles.common,
+ ...styles.dropdownButton,
+ ...buttonStyles,
+ }}
+ >
{state.jsonDownloading || state.tsvDownloading
? 'Processing'
: inactiveText}
- }
- dropdownStyle={{ ...styles.dropdownContainer, ...dropdownStyles }}
- >
+ )}
+ className="data-download-clinical"
+ dropdownStyle={{
+ ...styles.dropdownContainer,
+ ...dropdownStyles,
+ }}
+ >
- setState(s => ({
- ...s,
- tsvDownloading: currentState,
- }))}
- active={state.tsvDownloading}
- filters={filters}
+ className="data-download-clinical-tsv"
+ endpoint="/clinical_tar"
filename={tsvFilename}
+ filters={filters}
+ format="TSV"
+ inactiveText="TSV"
scope={scope}
- />
- setState(s => ({
+ ...s,
+ tsvDownloading: currentState,
+ }))}
size={clinicalCount}
style={styles.button(theme)}
- endpoint="/cases"
+ />
+
- setState(s => ({
- ...s,
- jsonDownloading: currentState,
- }))}
- active={state.jsonDownloading}
- filters={filters}
- fields={['case_id']}
+ className="data-download-clinical-json"
dataExportExpands={[
'demographic',
'diagnoses',
@@ -132,8 +122,18 @@ export default compose(
'family_histories',
'exposures',
]}
+ endpoint="/cases"
+ fields={['case_id']}
filename={jsonFilename}
- />
+ filters={filters}
+ inactiveText="JSON"
+ setParentState={currentState => setState(s => ({
+ ...s,
+ jsonDownloading: currentState,
+ }))}
+ size={clinicalCount}
+ style={styles.button(theme)}
+ />
);
},
diff --git a/src/packages/@ncigdc/modern_components/QQPlot/QQPlot.js b/src/packages/@ncigdc/modern_components/QQPlot/QQPlot.js
new file mode 100644
index 000000000..833a7bddf
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/QQPlot/QQPlot.js
@@ -0,0 +1,243 @@
+import React from 'react';
+import * as d3 from 'd3';
+import ReactFauxDOM from 'react-faux-dom';
+import {
+ compose,
+ withState,
+ pure,
+ withProps,
+} from 'recompose';
+import { last, groupBy, sortBy } from 'lodash';
+import reactSize from 'react-sizeme';
+
+import { qnorm } from './qqUtils';
+
+const QQPlot = ({
+ axisStyles = {},
+ data = [],
+ exportCoordinates = false,
+ height = 320,
+ plotTitle = '',
+ qqLineStyles = {},
+ qqPointStyles = {},
+ size: { width },
+ styles = {},
+ xAxisTitle = 'Theoretical Quantiles',
+ yAxisTitle = 'Sample Quantiles',
+}) => {
+ // default styles
+ const qqLine = {
+ color: '#e377c2',
+ strokeWidth: 2,
+ ...qqLineStyles,
+ };
+ const qqPoint = {
+ color: 'blue',
+ strokeWidth: 1,
+ radius: 1.5,
+ ...qqPointStyles,
+ };
+ const axisStyle = {
+ textColor: '#888',
+ fontSize: '1rem',
+ fontWeight: '400',
+ ...axisStyles,
+ };
+
+ const margin = styles.margin || {
+ top: 20,
+ right: 20,
+ bottom: 20,
+ left: 20,
+ };
+
+ const chartWidth = (width || 300) - margin.left - margin.right;
+ const chartHeight = height - margin.top - margin.bottom;
+ const padding = styles.padding || 40;
+
+ // qq plot calculations
+ const n = data.length;
+
+ // subtract 1 to account for 0 index array
+ const getQuantile = (count, quantile) => Math.ceil(count * (quantile / 4)) - 1;
+
+ let zScores = data;
+
+ // sample quantile(y) and theoretical quantile (x)
+ if (!exportCoordinates) {
+ zScores = sortBy(data).map((age, i) => ({
+ x: qnorm((i + 1 - 0.5) / n),
+ y: age,
+ }));
+ }
+
+ const quantile1Coords = zScores[getQuantile(n, 1)];
+ const quantile3Coords = zScores[getQuantile(n, 3)];
+
+ // create svg
+ const el = ReactFauxDOM.createElement('div');
+ el.style.width = '100%';
+ el.setAttribute('class', 'qq-plot');
+
+ const xScale = d3
+ .scaleLinear()
+ .domain([
+ d3.min(zScores, (d) => {
+ return Math.floor(d.x);
+ }),
+ d3.max(zScores, (d) => {
+ return Math.ceil(d.x);
+ }),
+ ])
+ .range([padding, chartWidth - padding * 2]);
+
+ const yScale = d3
+ .scaleLinear()
+ .domain([
+ d3.min(zScores, (d) => {
+ return Math.floor(d.y);
+ }),
+ d3.max(zScores, (d) => {
+ return Math.ceil(d.y);
+ }),
+ ])
+ .range([chartHeight, padding]);
+
+ const xAxis = d3
+ .axisBottom()
+ .scale(xScale)
+ .ticks(Object.keys(groupBy(zScores.map(z => z.x), Math.floor)).length);
+
+ const yAxis = d3
+ .axisLeft()
+ .scale(yScale)
+ .ticks(5);
+
+ // get slope from first and third quantile to match qqline from R
+ const slope = (quantile3Coords.y - quantile1Coords.y) / (quantile3Coords.x - quantile1Coords.x);
+ const yMin = zScores[0].y;
+ const yMax = last(zScores).y;
+
+ // calculate x values for start and end of line
+ const xAtYMin = quantile1Coords.x - ((quantile1Coords.y - yMin) / slope);
+ const xAtYMax = quantile3Coords.x + ((yMax - quantile3Coords.y) / slope);
+
+ const svg = d3
+ .select(el)
+ .append('svg')
+ .attr('width', chartWidth)
+ .attr('height', chartHeight + padding);
+
+ // draw sample points
+ svg
+ .selectAll('circle')
+ .data(zScores)
+ .enter()
+ .append('circle')
+ .attr('cx', (d) => xScale(d.x))
+ .attr('cy', (d) => yScale(d.y))
+ .attr('r', qqPoint.radius)
+ .attr('stroke', qqPoint.color)
+ .attr('stroke-width', qqPoint.strokeWidth)
+ .attr('fill', 'transparent')
+ .attr('transform', `translate(${padding},${-(padding / 2)})`);
+
+ const line = d3
+ .line()
+ .x(d => xScale(d.x))
+ .y(d => yScale(d.y));
+
+ // draw qq line
+ svg
+ .append('path')
+ .attr('class', 'coords')
+ .datum([
+ {
+ x: xAtYMin,
+ y: yMin,
+ },
+ ...quantile1Coords,
+ ...quantile3Coords,
+ {
+ x: xAtYMax,
+ y: yMax,
+ },
+ ])
+ .attr('d', line)
+ .attr('stroke', qqLine.color)
+ .attr('stroke-width', qqLine.strokeWidth)
+ .attr('transform', `translate(${padding},${-(padding / 2)})`);
+
+// clip path to prevent qq line extending beyond y-axis
+ svg
+ .append('rect')
+ .attr('x', -(padding))
+ .attr('y', padding / 2)
+ .attr('clip-path', 'url(#regression-clip-left)')
+ .style('fill', 'white')
+ .attr('height', chartHeight - padding)
+ .attr('width', padding * 2)
+ .attr('transform', `translate(${padding},${padding / 2})`);
+
+ // position slightly higher to account for qqline end
+ svg
+ .append('rect')
+ .attr('x', -(padding))
+ .attr('y', padding / 2)
+ .attr('clip-path', 'url(#regression-clip-right)')
+ .style('fill', 'white')
+ .attr('height', chartHeight)
+ .attr('width', padding * 2)
+ .attr('transform', `translate(${chartWidth},${-padding})`);
+
+ svg
+ .append('text')
+ .attr('y', 0)
+ .attr('x', (width / 2) - (padding / 2))
+ .attr('dy', '1em')
+ .style('text-anchor', 'middle')
+ .style('fontSize', '1.2rem')
+ .style('fontWeight', '400')
+ .style('marginBottom', 10)
+ .text(plotTitle);
+
+ // x axis
+ svg
+ .append('g')
+ .attr('class', 'x axis')
+ .attr('transform', `translate(${padding},${height - (padding / 2)})`)
+ .call(xAxis);
+
+ svg
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('transform', `translate(${(chartWidth + padding) / 2},${height + (padding / 2)})`)
+ .text(xAxisTitle)
+ .style('fontSize', axisStyle.fontSize)
+ .style('fontWeight', axisStyle.fontWeight)
+ .attr('fill', axisStyle.textColor);
+
+ // y axis
+ svg
+ .append('g')
+ .attr('class', 'y axis')
+ .attr('transform', `translate(${padding * 2}, ${-(padding / 2)})`)
+ .call(yAxis);
+
+ svg
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('transform', `translate(${padding - 10},${height / 2})rotate(-90)`)
+ .text(yAxisTitle)
+ .style('fontSize', axisStyle.fontSize)
+ .style('fontWeight', axisStyle.fontWeight)
+ .attr('fill', axisStyle.textColor);
+
+ return el.toReact();
+};
+
+export default reactSize(compose(
+ withState('chart', 'setState', ),
+ withProps(({ data }) => ({ data })),
+ pure
+))(QQPlot);
diff --git a/src/packages/@ncigdc/modern_components/QQPlot/QQPlotQuery.js b/src/packages/@ncigdc/modern_components/QQPlot/QQPlotQuery.js
new file mode 100644
index 000000000..a4b8e0745
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/QQPlot/QQPlotQuery.js
@@ -0,0 +1,140 @@
+import React from 'react';
+import {
+ compose, withPropsOnChange, withProps, withState,
+} from 'recompose';
+import {
+ sortBy, isArray, isPlainObject, isNumber, isEqual
+} from 'lodash';
+
+import { addInFilters } from '@ncigdc/utils/filters';
+import { fetchApi } from '@ncigdc/utils/ajax';
+import Spinner from '@ncigdc/uikit/Loaders/Material';
+import { withTheme } from '@ncigdc/theme';
+import { qnorm } from './qqUtils';
+import QQPlot from './QQPlot';
+
+export default compose(
+ withTheme,
+ withState('data', 'setData', null),
+ withState('isLoading', 'setIsLoading', true),
+ withProps({
+ updateData: async ({
+ dataHandler,
+ fieldName,
+ first,
+ filters,
+ setData,
+ setDataHandler,
+ setIsLoading,
+ }) => {
+ setIsLoading(true);
+ setDataHandler(false);
+ const missingFilter = {
+ op: 'and',
+ content: [
+ {
+ op: 'NOT',
+ content: {
+ field: `cases.${fieldName}`,
+ value: ['MISSING'],
+ },
+ },
+ ],
+ };
+ const newFilters = addInFilters(filters, missingFilter)
+ const res = await fetchApi('case_ssms', {
+ headers: { 'Content-Type': 'application/json' },
+ body: {
+ filters: JSON.stringify(newFilters),
+ size: first,
+ fields: fieldName,
+ },
+ });
+ const data = res && res.data ? res.data.hits : [];
+ const parsedFieldName = field => {
+ const parsed = field.split('.');
+ return {
+ clinicalType: parsed[0],
+ clinicalField: parsed[1],
+ clinicalNestedField: parsed[2],
+ };
+ };
+ const { clinicalField, clinicalNestedField, clinicalType } = parsedFieldName(fieldName);
+
+ const continuousValues = [];
+
+ data.forEach(hit => {
+ if (isArray(hit[clinicalType])) {
+ return hit[clinicalType].map(subType => {
+ if (clinicalNestedField) {
+ return subType[clinicalField].map(sub => {
+ return continuousValues.push(sub[clinicalNestedField]);
+ });
+ }
+ return continuousValues.push(subType[clinicalField]);
+ });
+ }
+ if (isPlainObject(hit[clinicalType])) {
+ return continuousValues.push(hit[clinicalType][clinicalField]);
+ }
+ return continuousValues.push(hit[clinicalType]);
+ });
+ const sortedData = sortBy(continuousValues.filter(b => isNumber(b)));
+ const parsedData = sortBy(sortedData).map((age, i) => ({
+ x: qnorm((i + 1 - 0.5) / sortedData.length),
+ y: age,
+ }));
+ setData(parsedData, () => setIsLoading(false));
+ dataHandler(parsedData.map(d => ({
+ 'Sample Quantile': d.y,
+ 'Theoretical Quantile': d.x,
+ })), () => setDataHandler(true));
+ },
+ }),
+ withPropsOnChange((props, nextProps) => !isEqual(props.dataBuckets, nextProps.dataBuckets), ({ updateData, ...props }) => updateData(props)),
+)(({
+ chartHeight,
+ clinicalType,
+ data,
+ fieldName,
+ isLoading,
+ queryField,
+ theme,
+ ...props
+}) => {
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+});
diff --git a/src/packages/@ncigdc/modern_components/QQPlot/qqUtils.js b/src/packages/@ncigdc/modern_components/QQPlot/qqUtils.js
new file mode 100644
index 000000000..f1a404422
--- /dev/null
+++ b/src/packages/@ncigdc/modern_components/QQPlot/qqUtils.js
@@ -0,0 +1,47 @@
+// from https://rangevoting.org/Qnorm.html
+export const qnorm = function (p) {
+ // ALGORITHM AS 111, APPL.STATIST., VOL.26, 118-121, 1977.
+ // Computes z = invNorm(p)
+
+ p = parseFloat(p);
+ const split = 0.42;
+
+ const a0 = 2.50662823884;
+ const a1 = -18.61500062529;
+ const a2 = 41.39119773534;
+ const a3 = -25.44106049637;
+ const b1 = -8.4735109309;
+ const b2 = 23.08336743743;
+ const b3 = -21.06224101826;
+ const b4 = 3.13082909833;
+ const c0 = -2.78718931138;
+ const c1 = -2.29796479134;
+ const c2 = 4.85014127135;
+ const c3 = 2.32121276858;
+ const d1 = 3.54388924762;
+ const d2 = 1.63706781897;
+
+ const q = p - 0.5;
+
+ let r;
+ let ppnd;
+
+ if (Math.abs(q) <= split) {
+ r = q * q;
+ ppnd =
+ (q * (((a3 * r + a2) * r + a1) * r + a0)) /
+ ((((b4 * r + b3) * r + b2) * r + b1) * r + 1);
+ } else {
+ r = p;
+ if (q > 0) r = 1 - p;
+ if (r > 0) {
+ r = Math.sqrt(-Math.log(r));
+ ppnd = (((c3 * r + c2) * r + c1) * r + c0) / ((d2 * r + d1) * r + 1);
+ if (q < 0) ppnd = -ppnd;
+ } else {
+ ppnd = 0;
+ }
+ }
+
+ return ppnd;
+};
diff --git a/src/packages/@ncigdc/modern_components/withSetAction/SetActionButton.js b/src/packages/@ncigdc/modern_components/withSetAction/SetActionButton.js
index 6b4c14c68..39c687fdc 100644
--- a/src/packages/@ncigdc/modern_components/withSetAction/SetActionButton.js
+++ b/src/packages/@ncigdc/modern_components/withSetAction/SetActionButton.js
@@ -3,7 +3,7 @@ import Button from '@ncigdc/uikit/Button';
import Overlay from '@ncigdc/uikit/Overlay';
import Spinner from '@ncigdc/uikit/Loaders/Material';
-export default class extends React.Component {
+export default class SetActionButton extends React.Component {
clicked = false;
componentWillReceiveProps(next) {
@@ -39,7 +39,8 @@ export default class extends React.Component {
onClick();
createSet(props);
}}
- style={style}>
+ style={style}
+ >
{children}
diff --git a/src/packages/@ncigdc/modern_components/withSetAction/index.js b/src/packages/@ncigdc/modern_components/withSetAction/index.js
index 7469966f5..335e4f6ae 100644
--- a/src/packages/@ncigdc/modern_components/withSetAction/index.js
+++ b/src/packages/@ncigdc/modern_components/withSetAction/index.js
@@ -1,62 +1,111 @@
// @flow
-import { withProps, compose } from 'recompose';
+import {
+ compose,
+ setDisplayName,
+ withProps,
+} from 'recompose';
import withSetAction from './withSetAction';
import SetActionButton from './SetActionButton';
export const CreateExploreGeneSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'gene', scope: 'explore', action: 'create' })),
+ withProps(() => ({
+ type: 'gene',
+ scope: 'explore',
+ action: 'create',
+ })),
)(SetActionButton);
export const CreateExploreCaseSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'case', scope: 'explore', action: 'create' })),
+ withProps(() => ({
+ type: 'case',
+ scope: 'explore',
+ action: 'create',
+ })),
)(SetActionButton);
export const CreateExploreSsmSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'ssm', scope: 'explore', action: 'create' })),
+ withProps(() => ({
+ type: 'ssm',
+ scope: 'explore',
+ action: 'create',
+ })),
)(SetActionButton);
export const CreateRepositoryCaseSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'case', scope: 'repository', action: 'create' })),
+ withProps(() => ({
+ type: 'case',
+ scope: 'repository',
+ action: 'create',
+ })),
)(SetActionButton);
export const AppendExploreGeneSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'gene', scope: 'explore', action: 'append' })),
+ withProps(() => ({
+ type: 'gene',
+ scope: 'explore',
+ action: 'append',
+ })),
)(SetActionButton);
export const AppendExploreCaseSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'case', scope: 'explore', action: 'append' })),
+ withProps(() => ({
+ type: 'case',
+ scope: 'explore',
+ action: 'append',
+ })),
)(SetActionButton);
export const AppendExploreSsmSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'ssm', scope: 'explore', action: 'append' })),
+ withProps(() => ({
+ type: 'ssm',
+ scope: 'explore',
+ action: 'append',
+ })),
)(SetActionButton);
export const AppendRepositoryCaseSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'case', scope: 'repository', action: 'append' })),
+ withProps(() => ({
+ type: 'case',
+ scope: 'repository',
+ action: 'append',
+ })),
)(SetActionButton);
export const RemoveFromExploreGeneSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'gene', scope: 'explore', action: 'remove_from' })),
+ withProps(() => ({
+ type: 'gene',
+ scope: 'explore',
+ action: 'remove_from',
+ })),
)(SetActionButton);
export const RemoveFromExploreCaseSetButton = compose(
+ setDisplayName('EnhancedRemoveFromExploreCaseSetButton'),
withSetAction,
- withProps(() => ({ type: 'case', scope: 'explore', action: 'remove_from' })),
+ withProps(() => ({
+ type: 'case',
+ scope: 'explore',
+ action: 'remove_from',
+ })),
)(SetActionButton);
export const RemoveFromExploreSsmSetButton = compose(
withSetAction,
- withProps(() => ({ type: 'ssm', scope: 'explore', action: 'remove_from' })),
+ withProps(() => ({
+ type: 'ssm',
+ scope: 'explore',
+ action: 'remove_from',
+ })),
)(SetActionButton);
export const RemoveFromRepositoryCaseSetButton = compose(
diff --git a/src/packages/@ncigdc/tableModels/utils.tsx b/src/packages/@ncigdc/tableModels/utils.tsx
index 9400e5617..a12fad966 100644
--- a/src/packages/@ncigdc/tableModels/utils.tsx
+++ b/src/packages/@ncigdc/tableModels/utils.tsx
@@ -3,12 +3,17 @@ import _ from 'lodash';
import Tooltip from '@ncigdc/uikit/Tooltip/Tooltip';
import Hidden from '@ncigdc/components/Hidden';
import { tableToolTipHint } from '@ncigdc/theme/mixins';
-import { DATA_CATEGORIES } from '@ncigdc/utils/constants';
+import { DATA_CATEGORIES, DATA_CATEGORIES_FOR_PROJECTS_TABLE } from '@ncigdc/utils/constants';
import { Th, Td, ThNum, TdNum } from '@ncigdc/uikit/Table';
import { makeFilter } from '@ncigdc/utils/filters';
import { findDataCategory } from '@ncigdc/utils/data';
import { IListLinkProps } from '@ncigdc/components/Links/types';
-import { TCategoryAbbr, IDataCategory } from '@ncigdc/utils/data/types';
+import ProjectLink from '@ncigdc/components/Links/ProjectLink';
+import {
+ IDataCategory,
+ TCategoryAbbr,
+} from '@ncigdc/utils/data/types';
+
interface ICreateDataCategoryColumnsProps{
title: string;
countKey: string;
@@ -79,6 +84,8 @@ export const createDataCategoryColumns = ({
getCellLinkFilters,
getTotalLinkFilters,
}: ICreateDataCategoryColumnsProps)=> {
+ const isProjectsTable = countKey === 'case_count';
+ const CATEGORY_COLUMNS = isProjectsTable ? DATA_CATEGORIES_FOR_PROJECTS_TABLE : DATA_CATEGORIES;
return [
{
name: 'Data Categories',
@@ -87,16 +94,21 @@ export const createDataCategoryColumns = ({
th: () => (
{title}
),
downloadable: true,
- subHeadingIds: _.map(DATA_CATEGORIES, category => category.abbr),
+ subHeadingIds: _.map(CATEGORY_COLUMNS, category => category.abbr),
},
- ..._.map(DATA_CATEGORIES, (category: { full: string; abbr: TCategoryAbbr }) => ({
+ ..._.map(CATEGORY_COLUMNS, (category: {
+ dataCategory: TCategoryAbbr,
+ full: string,
+ tooltip: string,
+ abbr: TCategoryAbbr,
+ }) => ({
name: category.abbr,
id: category.abbr,
subHeading: true,
@@ -104,21 +116,28 @@ export const createDataCategoryColumns = ({
th: () => (
-
+
{category.abbr}
),
td: ({ node }: { node: INode }) => {
- const count = findDataCategory(
- category.abbr,
- node.summary.data_categories
- )[countKey];
+ const isMetadataColumn = ['Clinical Metadata', 'Biospecimen Metadata'].includes(category.tooltip);
+ const count = isMetadataColumn
+ ? node.summary.file_count
+ : findDataCategory(
+ category.dataCategory || category.abbr,
+ node.summary.data_categories
+ )[countKey];
return (
{count === 0 ? (
'0'
+ ) : isMetadataColumn ? (
+
+ {node.summary.case_count.toLocaleString()}
+
) : (
- findDataCategory(category.abbr, x.node.summary.data_categories)[
- countKey
- ]
- ).toLocaleString()}
+ findDataCategory(
+ category.dataCategory || category.abbr,
+ x.node.summary.data_categories)[countKey]
+ ).toLocaleString()}
),
diff --git a/src/packages/@ncigdc/theme/fonts/gdc-icons.icomoon.selection.json b/src/packages/@ncigdc/theme/fonts/gdc-icons.icomoon.selection.json
index 1dafdcdea..7377fc3a1 100755
--- a/src/packages/@ncigdc/theme/fonts/gdc-icons.icomoon.selection.json
+++ b/src/packages/@ncigdc/theme/fonts/gdc-icons.icomoon.selection.json
@@ -322,7 +322,7 @@
"width": 1045,
"grid": 0,
"tags": [
- "GDC-App-data-transer-tool"
+ "GDC-App-data-transfer-tool"
],
"colorPermutations": {
"11631621621163163163118918518512552552551": [
@@ -378,7 +378,7 @@
59670,
59671
],
- "name": "gdc-data-transer-tool"
+ "name": "gdc-data-transfer-tool"
},
"setIdx": 1,
"setId": 2,
diff --git a/src/packages/@ncigdc/theme/global.css b/src/packages/@ncigdc/theme/global.css
index 190501f95..81b30ebe9 100644
--- a/src/packages/@ncigdc/theme/global.css
+++ b/src/packages/@ncigdc/theme/global.css
@@ -6668,41 +6668,41 @@ ul.sort-list .dndDragging {
margin-left: -1.02050781em;
color: #a3a3a3;
}
-.icon-gdc-data-transer-tool .path1:before {
+.icon-gdc-data-transfer-tool .path1:before {
content: "\e910";
color: #ffffff;
}
-.icon-gdc-data-transer-tool .path2:before {
+.icon-gdc-data-transfer-tool .path2:before {
content: "\e911";
margin-left: -1.02050781em;
color: #ffffff;
}
-.icon-gdc-data-transer-tool .path3:before {
+.icon-gdc-data-transfer-tool .path3:before {
content: "\e912";
margin-left: -1.02050781em;
color: #ffffff;
}
-.icon-gdc-data-transer-tool .path4:before {
+.icon-gdc-data-transfer-tool .path4:before {
content: "\e913";
margin-left: -1.02050781em;
color: #a3a3a3;
}
-.icon-gdc-data-transer-tool .path5:before {
+.icon-gdc-data-transfer-tool .path5:before {
content: "\e914";
margin-left: -1.02050781em;
color: #a3a3a3;
}
-.icon-gdc-data-transer-tool .path6:before {
+.icon-gdc-data-transfer-tool .path6:before {
content: "\e915";
margin-left: -1.02050781em;
color: #000000;
}
-.icon-gdc-data-transer-tool .path7:before {
+.icon-gdc-data-transfer-tool .path7:before {
content: "\e916";
margin-left: -1.02050781em;
color: #ffffff;
}
-.icon-gdc-data-transer-tool .path8:before {
+.icon-gdc-data-transfer-tool .path8:before {
content: "\e917";
margin-left: -1.02050781em;
color: #ffffff;
@@ -7008,6 +7008,10 @@ ul.sort-list .dndDragging {
padding: 2px;
height: 25px;
}
+.icon-gdc-publications {
+ padding: 2px;
+ height: 30px;
+}
.list-indent {
margin-right: 10px;
background: #5b5151;
@@ -8574,3 +8578,19 @@ fieldset[disabled] .btn-primary.active {
cursor: pointer;
background-color: #dbdbdb;
}
+
+#projects-table th {
+ word-wrap: break-word !important;
+ white-space: normal !important;
+ vertical-align: bottom;
+ max-width: 100px !important;
+}
+
+#projects-table td {
+ white-space: normal !important;
+ max-width: 100px !important;
+}
+
+abbr span {
+ display: inline !important;
+}
diff --git a/src/packages/@ncigdc/theme/images/GDC-App-publications.svg b/src/packages/@ncigdc/theme/images/GDC-App-publications.svg
new file mode 100644
index 000000000..f5f60f612
--- /dev/null
+++ b/src/packages/@ncigdc/theme/images/GDC-App-publications.svg
@@ -0,0 +1 @@
+icon-publications
\ No newline at end of file
diff --git a/src/packages/@ncigdc/theme/mixins.js b/src/packages/@ncigdc/theme/mixins.js
index 42565a782..41c48c21f 100644
--- a/src/packages/@ncigdc/theme/mixins.js
+++ b/src/packages/@ncigdc/theme/mixins.js
@@ -2,8 +2,8 @@
import { css } from 'glamor';
-import { getTheme } from './index';
import Color from 'color';
+import { getTheme } from './index';
export const center = {
display: 'flex',
@@ -31,6 +31,7 @@ export const dropdown = {
backgroundColor: 'white',
textAlign: 'left',
right: 0,
+ borderRadius: '5px',
};
export const dropdownButton = {
@@ -57,9 +58,9 @@ export const visualizingButton = {
export const margin = (left, right) => {
if (left && right) {
return { margin: '0 0.5rem' };
- } else if (left) {
+ } if (left) {
return { marginLeft: '0.5rem' };
- } else if (right) {
+ } if (right) {
return { marginRight: '0.5rem' };
}
return {};
@@ -125,10 +126,9 @@ export const linkButton = {
color: 'white',
},
':hover': {
- backgroundColor: ({ theme }: Object) =>
- Color(theme.primary)
- .lighten(0.7)
- .rgbString(),
+ backgroundColor: ({ theme }: Object) => Color(theme.primary)
+ .lighten(0.7)
+ .rgbString(),
color: 'white',
},
};
diff --git a/src/packages/@ncigdc/uikit/ControlEditableRow.js b/src/packages/@ncigdc/uikit/ControlEditableRow.js
new file mode 100644
index 000000000..a026d60e1
--- /dev/null
+++ b/src/packages/@ncigdc/uikit/ControlEditableRow.js
@@ -0,0 +1,141 @@
+import React from 'react';
+import {
+ compose,
+ withState,
+ withHandlers,
+} from 'recompose';
+
+import Input from '@ncigdc/uikit/Form/Input';
+import Pencil from '@ncigdc/theme/icons/Pencil';
+import { Row, Column } from '@ncigdc/uikit/Flex';
+import OutsideClickHandler from 'react-outside-click-handler';
+
+export default compose(
+ withState('isEditing', 'setIsEditing', ({ isEditing }: any) => isEditing || false),
+ withState('value', 'setValue', ({ text }: any) => text),
+ withHandlers({
+ handleCancel: ({
+ cleanWarning,
+ setIsEditing,
+ setValue,
+ text,
+ }: any) => () => {
+ setIsEditing(false);
+ setValue(text);
+ cleanWarning();
+ },
+ toggleEditingAndSave: ({
+ disabled = false,
+ handleSave,
+ isEditing,
+ setIsEditing,
+ value,
+ }: any) => () => {
+ if (disabled) {
+ return;
+ }
+ if (value.length !== 0) {
+ if (isEditing) {
+ if (handleSave(value) !== 'unsave') {
+ setIsEditing(false);
+ }
+ } else {
+ setIsEditing(true);
+ }
+ }
+ },
+ }),
+)(
+ ({
+ isEditing,
+ toggleEditingAndSave,
+ value,
+ setValue,
+ handleCancel,
+ children,
+ iconStyle = {},
+ containerStyle = {},
+ disabled = false,
+ noEditingStyle,
+ disableOnKeyDown = false,
+ warning,
+ onEdit,
+ }): any => {
+ return (
+
+ {isEditing ? (
+ {
+ if (disableOnKeyDown) {
+ return;
+ }
+ toggleEditingAndSave();
+ }}
+ >
+
+
+ {
+ onEdit(e.target.value);
+ setValue(e.target.value);
+ }}
+ onFocus={e => e.target.select()}
+ onKeyDown={e => {
+ if (!disableOnKeyDown && (e.key === 'Enter')) {
+ toggleEditingAndSave();
+ } else if (e.key === 'Escape') {
+ handleCancel();
+ }
+ }}
+ style={{
+ borderRadius: '4px',
+ transition: 'all 0.2s ease',
+ width: '300px',
+ }}
+ type="text"
+ value={value}
+ />
+
+ {warning
+ ? (
+
+ {warning}
+
+ ) : null}
+
+
+ )
+ : (
+
+ {children}
+
+
+ )
+ }
+
+ );
+ }
+);
diff --git a/src/packages/@ncigdc/uikit/Dropdown.js b/src/packages/@ncigdc/uikit/Dropdown.js
index 135f8563c..55266ebb0 100644
--- a/src/packages/@ncigdc/uikit/Dropdown.js
+++ b/src/packages/@ncigdc/uikit/Dropdown.js
@@ -20,9 +20,12 @@ const Dropdown = ({
className,
}) => (
+ className={`${className} dropdown`}
+ style={{
+ position: 'relative',
+ ...style,
+ }}
+ >
!isDisabled && setActive(!active)}>
{button || (
@@ -34,9 +37,12 @@ const Dropdown = ({
{active && (
!autoclose && e.stopPropagation()}
- >
+ style={{
+ ...styles.dropdown,
+ ...dropdownStyle,
+ }}
+ >
{children}
)}
diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js
index 556f31939..61e197b70 100644
--- a/src/packages/@ncigdc/uikit/EditableLabel.js
+++ b/src/packages/@ncigdc/uikit/EditableLabel.js
@@ -71,81 +71,81 @@ export default compose(
disabled = false,
disabledMessage = null,
}) => (
-
- )
-);
+ ) : (
+
+
+ {children}
+
+
+
+ )}
+
+ )
+);
\ No newline at end of file
diff --git a/src/packages/@ncigdc/uikit/Modal.js b/src/packages/@ncigdc/uikit/Modal.js
index 9900e2fff..bcdd684ed 100644
--- a/src/packages/@ncigdc/uikit/Modal.js
+++ b/src/packages/@ncigdc/uikit/Modal.js
@@ -26,21 +26,23 @@ const modalStyles = {
borderRadius: '4px',
margin: '30px auto',
padding: '0px',
- width: '65%',
+ width: '55%',
boxShadow: 'rgba(0, 0, 0, 0.5) 0px 5px 15px',
},
};
-const Modal = ({ isOpen, onRequestClose, style, children }) => (
+const Modal = ({
+ children, isOpen, onRequestClose, style,
+}) => (
{})}
- contentLabel="Modal"
- className="test-modal"
- >
- {Children.map(children, child => cloneElement(child, { ...child.props }))}
-
+ className="test-modal"
+ contentLabel="Modal"
+ isOpen={isOpen}
+ onRequestClose={onRequestClose || (() => { })}
+ style={{ ..._.merge({}, modalStyles, style) }}
+ >
+ {Children.map(children, child => cloneElement(child, { ...child.props }))}
+
);
export default Modal;
diff --git a/src/packages/@ncigdc/uikit/Tabs.js b/src/packages/@ncigdc/uikit/Tabs.js
index 55ebee87c..ef1f767ad 100644
--- a/src/packages/@ncigdc/uikit/Tabs.js
+++ b/src/packages/@ncigdc/uikit/Tabs.js
@@ -1,17 +1,10 @@
-// @flow
-
-// Vendor
import React, { Children } from 'react';
import PropTypes from 'prop-types';
import Color from 'color';
import { css } from 'glamor';
import { withTheme } from '@ncigdc/theme';
-
-// Custom
import { Row, Column } from './Flex';
-/*----------------------------------------------------------------------------*/
-
const borderStyle = theme => `1px solid ${theme.greyScale4}`;
const tabBorder = (theme, side) => ({
@@ -21,53 +14,49 @@ const tabBorder = (theme, side) => ({
borderBottom: side && borderStyle(theme),
});
-const baseTabStyle = (theme, side) =>
- css({
- padding: '1.2rem 1.8rem',
- fontSize: '1.5rem',
- color: '#000',
- textDecoration: 'none',
- borderTop: '1px solid transparent',
- borderLeft: '1px solid transparent',
- borderBottom: '1px solid transparent',
- borderRight: '1px solid transparent',
- backgroundColor: theme.greyScale6,
- marginBottom: !side && '-1px',
- marginRight: side && '-1px',
- transition: 'background-color 0.2s ease',
- borderRadius: side ? '4px 0 0 4px' : '4px 4px 0 0',
- cursor: 'pointer',
- overflow: 'hidden',
- });
+const baseTabStyle = (theme, side) => css({
+ padding: '1.2rem 1.8rem',
+ fontSize: '1.5rem',
+ color: '#000',
+ textDecoration: 'none',
+ borderTop: '1px solid transparent',
+ borderLeft: '1px solid transparent',
+ borderBottom: '1px solid transparent',
+ borderRight: '1px solid transparent',
+ backgroundColor: theme.greyScale6,
+ marginBottom: !side && '-1px',
+ marginRight: side && '-1px',
+ transition: 'background-color 0.2s ease',
+ borderRadius: side ? '4px 0 0 4px' : '4px 4px 0 0',
+ cursor: 'pointer',
+ overflow: 'hidden',
+});
const styles = {
- active: (theme, side) =>
- css({
- backgroundColor: '#fff',
+ active: (theme, side) => css({
+ backgroundColor: '#fff',
+ ...tabBorder(theme, side),
+ position: 'relative',
+ left: '0px',
+ zIndex: 2,
+ ':hover': {
+ backgroundColor: 'white',
+ },
+ }),
+ inactive: (theme, side) => css({
+ ':hover': {
+ textDecoration: 'none',
+ color: '#000',
+ backgroundColor: Color(theme.greyScale6)
+ .darken(0.05)
+ .rgbString(),
...tabBorder(theme, side),
- position: 'relative',
- left: '0px',
- zIndex: 2,
- ':hover': {
- backgroundColor: 'white',
- },
- }),
- inactive: (theme, side) =>
- css({
- ':hover': {
- textDecoration: 'none',
- color: '#000',
- backgroundColor: Color(theme.greyScale6)
- .darken(0.05)
- .rgbString(),
- ...tabBorder(theme, side),
- },
- }),
- margin: side =>
- css({
- marginLeft: !side && '0.4rem',
- marginTop: side && '0.4rem',
- }),
+ },
+ }),
+ margin: side => css({
+ marginLeft: !side && '0.4rem',
+ marginTop: side && '0.4rem',
+ }),
content: theme => ({
border: borderStyle(theme),
backgroundColor: '#fff',
@@ -89,26 +78,26 @@ const Tab = ({
{...(sibling ? styles.margin(side) : {})}
style={tabStyle}
{...props}
- >
+ >
{children}
);
const Tabs = ({
- style,
- tabs,
activeIndex,
children,
+ contentStyle,
onTabClick,
side,
- contentStyle,
- theme,
+ style,
tabStyle,
tabToolbar,
+ tabs,
+ theme,
...props
-}) =>
- side ? (
-
+}) => (side
+ ? (
+
+ >
{Children.map(tabs, (child, i) => (
(onTabClick ? onTabClick(i) : () => {})}
- active={i === activeIndex}
sibling={i}
- theme={theme}
- tabStyle={tabStyle}
side
- >
+ tabStyle={tabStyle}
+ theme={theme}
+ >
{child}
))}
@@ -145,41 +134,43 @@ const Tabs = ({
width: 1,
...(contentStyle || {}),
}}
- >
+ >
{children}
) : (
-
+
{Children.map(tabs, (child, i) => (
(onTabClick ? onTabClick(i) : () => {})}
- active={i === activeIndex}
- sibling={i}
- theme={theme}
- tabStyle={tabStyle}
- side={false}
- >
+ active={i === activeIndex}
+ className="test-tab"
+ onClick={() => (onTabClick ? onTabClick(i) : () => {})}
+ sibling={i}
+ side={false}
+ tabStyle={tabStyle}
+ theme={theme}
+ >
{child}
))}
{tabToolbar && {tabToolbar} }
-
+
{children}
- );
+ ));
Tabs.propTypes = {
- children: PropTypes.node,
activeIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ children: PropTypes.node,
style: PropTypes.object,
tabs: PropTypes.node,
};
-/*----------------------------------------------------------------------------*/
-
export default withTheme(Tabs);
diff --git a/src/packages/@ncigdc/uikit/Tooltip/index.js b/src/packages/@ncigdc/uikit/Tooltip/index.js
index 165f208e0..164331518 100644
--- a/src/packages/@ncigdc/uikit/Tooltip/index.js
+++ b/src/packages/@ncigdc/uikit/Tooltip/index.js
@@ -1,8 +1,4 @@
-// @flow
-
-import GlobalTooltip from './GlobalTooltip';
-import Tooltip from './Tooltip';
-import tooltipReducer, { setTooltip } from './dux';
-import withTooltip from './withTooltip';
-
-export { GlobalTooltip, Tooltip, tooltipReducer, setTooltip, withTooltip };
+export { default as GlobalTooltip } from './GlobalTooltip';
+export { default as Tooltip } from './Tooltip';
+export { default as tooltipReducer, setTooltip } from './dux';
+export { default as withTooltip, TooltipInjector } from './withTooltip';
diff --git a/src/packages/@ncigdc/uikit/Tooltip/withTooltip.js b/src/packages/@ncigdc/uikit/Tooltip/withTooltip.js
index be042f488..d8850d2ce 100644
--- a/src/packages/@ncigdc/uikit/Tooltip/withTooltip.js
+++ b/src/packages/@ncigdc/uikit/Tooltip/withTooltip.js
@@ -2,16 +2,18 @@
import React from 'react';
import {
compose,
- withState,
lifecycle,
- withHandlers,
mapProps,
+ setDisplayName,
+ withHandlers,
+ withState,
} from 'recompose';
import { connect } from 'react-redux';
-import { setTooltip } from '@ncigdc/uikit/Tooltip';
import { omit } from 'lodash';
+import { setTooltip } from './dux';
const enhance = compose(
+ setDisplayName('TooltipEnhancer'),
connect(),
withState('tooltipState', 'setTooltipState', false),
withHandlers({
@@ -27,11 +29,20 @@ const enhance = compose(
}
},
}),
- mapProps(props =>
- omit(props, ['tooltipState', 'setTooltipState', 'dispatch']),
- ),
+ mapProps(props => omit(props, [
+ 'tooltipState',
+ 'setTooltipState',
+ 'dispatch',
+ ]),),
);
const withTooltip = Wrapped => enhance(props => );
+export const TooltipInjector = enhance(({
+ children,
+ ...props
+}) => React.cloneElement(
+ children, props
+));
+
export default withTooltip;
diff --git a/src/packages/@ncigdc/uikit/UnstyledButton.js b/src/packages/@ncigdc/uikit/UnstyledButton.js
index 9b18dc767..25e6f7787 100644
--- a/src/packages/@ncigdc/uikit/UnstyledButton.js
+++ b/src/packages/@ncigdc/uikit/UnstyledButton.js
@@ -13,8 +13,17 @@ const base = {
padding: 0,
};
-export default ({ children, style, ...props }) => (
-
+const UnstyledButton = ({ children, style, ...props }) => (
+
{children}
);
+
+export default UnstyledButton;
diff --git a/src/packages/@ncigdc/utils/ageDisplay.js b/src/packages/@ncigdc/utils/ageDisplay.js
index ed01df8c3..f696e815d 100644
--- a/src/packages/@ncigdc/utils/ageDisplay.js
+++ b/src/packages/@ncigdc/utils/ageDisplay.js
@@ -3,19 +3,17 @@
import _ from 'lodash';
export const DAYS_IN_YEAR = 365.25;
-export const getLowerAgeYears = (days: number) =>
- Math.ceil(days / DAYS_IN_YEAR);
-export const getUpperAgeYears = (days: number) =>
- Math.ceil((days + 1 - DAYS_IN_YEAR) / DAYS_IN_YEAR);
+export const getLowerAgeYears = (days: number) => Math.ceil(days / DAYS_IN_YEAR);
+export const getUpperAgeYears = (days: number) => Math.ceil((days + 1 - DAYS_IN_YEAR) / DAYS_IN_YEAR);
export default (
ageInDays: number,
yearsOnly: boolean = false,
defaultValue: string = '--',
): string => {
- const DAYS_IN_YEAR = 365.25;
- const leapThenPair = (years: number, days: number): number[] =>
- days === 365 ? [years + 1, 0] : [years, days];
+ const leapThenPair = (years: number, days: number): number[] => (
+ days === 365 ? [years + 1, 0] : [years, days]
+ );
const timeString = (
num: number,
singular: string,
diff --git a/src/packages/@ncigdc/utils/constants.js b/src/packages/@ncigdc/utils/constants.js
index bc335e6e0..ebcab40fb 100644
--- a/src/packages/@ncigdc/utils/constants.js
+++ b/src/packages/@ncigdc/utils/constants.js
@@ -48,18 +48,72 @@ export const LOCAL_STORAGE_API_OVERRIDE = API_OVERRIDE_KEYS.some(
k => localStorage[k]
);
+const DATA_CATEGORIES_COMMON = {
+ SEQ: {
+ full: 'Sequencing Reads',
+ abbr: 'Seq',
+ },
+ EXP: {
+ full: 'Transcriptome Profiling',
+ abbr: 'Exp',
+ },
+ SNV: {
+ full: 'Simple Nucleotide Variation',
+ abbr: 'SNV',
+ },
+ CNV: {
+ full: 'Copy Number Variation',
+ abbr: 'CNV',
+ },
+ METH: {
+ full: 'DNA Methylation',
+ abbr: 'Meth',
+ },
+};
+
export const DATA_CATEGORIES = {
- SEQ: { full: 'Sequencing Reads', abbr: 'Seq' },
- EXP: { full: 'Transcriptome Profiling', abbr: 'Exp' },
- SNV: { full: 'Simple Nucleotide Variation', abbr: 'SNV' },
- CNV: { full: 'Copy Number Variation', abbr: 'CNV' },
- METH: { full: 'DNA Methylation', abbr: 'Meth' },
- CLINICAL: { full: 'Clinical', abbr: 'Clinical' },
- BIOSPECIMEN: { full: 'Biospecimen', abbr: 'Bio' },
+ ...DATA_CATEGORIES_COMMON,
+ CLINICAL: {
+ full: 'Clinical',
+ abbr: 'Clinical',
+ },
+ BIOSPECIMEN: {
+ full: 'Biospecimen',
+ abbr: 'Bio',
+ },
+};
+
+export const DATA_CATEGORIES_FOR_PROJECTS_TABLE = {
+ ...DATA_CATEGORIES_COMMON,
+ CLINICAL_METADATA: {
+ full: '',
+ abbr: 'Clinical',
+ tooltip: 'Clinical Metadata',
+ },
+ CLINICAL_SUPPLEMENT: {
+ full: 'Clinical',
+ dataCategory: 'Clinical',
+ abbr: 'Clinical Supplement',
+ tooltip: 'Clinical Supplement',
+ },
+ BIOSPECIMEN_METADATA: {
+ full: '',
+ abbr: 'Bio',
+ tooltip: 'Biospecimen Metadata',
+ },
+ BIOSPECIMEN_SUPPLEMENT: {
+ full: 'Biospecimen',
+ dataCategory: 'Bio',
+ abbr: 'Bio Supplement',
+ tooltip: 'Biospecimen Supplement',
+ },
};
export const DATA_TYPES = {
- GEQ: { full: 'Gene Expression Quantification', abbr: 'GEQ' },
+ GEQ: {
+ full: 'Gene Expression Quantification',
+ abbr: 'GEQ',
+ },
};
export const EXPERIMENTAL_STRATEGIES = [
@@ -115,7 +169,11 @@ export const MUTATION_SUBTYPE_MAP = {
'small insertion': 'Insertion',
};
-export const DNA_CHANGE_MARKERS = ['del', 'ins', '>'];
+export const DNA_CHANGE_MARKERS = [
+ 'del',
+ 'ins',
+ '>',
+];
export const HUMAN_BODY_SITES_MAP = {
'accessory sinuses': 'Head and Neck',
@@ -287,20 +345,41 @@ export const IMPACT_SHORT_FORMS = {
export const FAKE_USER =
localStorage.REACT_APP_ALLOW_FAKE_USER || process.env.REACT_APP_ALLOW_FAKE_USER
- ? {
+ ? {
username: 'DEV_USER',
projects: {
phs_ids: {
- phs000178: ['_member_', 'read', 'delete'],
+ phs000178: [
+ '_member_',
+ 'read',
+ 'delete',
+ ],
},
gdc_ids: {
- 'TCGA-LIHC': ['read', 'delete', 'create', 'update', 'read_report'],
- 'CGCI-BLGSP': ['create', 'update', 'release', 'read_report'],
- 'TCGA-DEV3': ['read', 'create', 'update', 'release', 'delete'],
+ 'TCGA-LIHC': [
+ 'read',
+ 'delete',
+ 'create',
+ 'update',
+ 'read_report',
+ ],
+ 'CGCI-BLGSP': [
+ 'create',
+ 'update',
+ 'release',
+ 'read_report',
+ ],
+ 'TCGA-DEV3': [
+ 'read',
+ 'create',
+ 'update',
+ 'release',
+ 'delete',
+ ],
},
},
}
- : null;
+ : null;
export const IS_DEV = process.env.NODE_ENV === 'development';
@@ -324,4 +403,11 @@ export const CLINICAL_BLACKLIST = [
export const IS_CDAVE_DEV =
localStorage.REACT_APP_IS_CDAVE_DEV ||
process.env.NODE_ENV === 'development' ||
- false;
\ No newline at end of file
+ false;
+
+export const analysisColors = {
+ Demographic: '#1f77b4',
+ Diagnosis: '#ff7f0e',
+ Treatment: '#2ca02c',
+ Exposure: '#9467bd',
+};
diff --git a/src/packages/@ncigdc/utils/data/types.ts b/src/packages/@ncigdc/utils/data/types.ts
index 6106c9a89..070283dc0 100644
--- a/src/packages/@ncigdc/utils/data/types.ts
+++ b/src/packages/@ncigdc/utils/data/types.ts
@@ -15,13 +15,13 @@ export type TCategory =
| 'Biospecimen';
export type TCategoryAbbr =
- | 'Seq'
- | 'Exp'
- | 'SNV'
- | 'CNV'
- | 'Clinical'
| 'Bio'
- | 'Meth';
+ | 'Clinical'
+ | 'CNV'
+ | 'Exp'
+ | 'Meth'
+ | 'Seq'
+ | 'SNV';
export type TCategoryMap = { [k in TCategoryAbbr]: TCategory };
diff --git a/src/packages/@ncigdc/utils/filters/index.ts b/src/packages/@ncigdc/utils/filters/index.ts
index 44e7954a1..b859384ed 100644
--- a/src/packages/@ncigdc/utils/filters/index.ts
+++ b/src/packages/@ncigdc/utils/filters/index.ts
@@ -19,8 +19,8 @@ import {
function compareTerms(a: IValueFilter, b: IValueFilter): boolean {
return (
- a.content.field === b.content.field &&
- a.op.toLowerCase() === b.op.toLowerCase()
+ a.content && (a.content.field === b.content.field) &&
+ a.op && (a.op.toLowerCase() === b.op.toLowerCase())
);
}
@@ -82,7 +82,6 @@ const replace: TMergeFilters = (x, y) => ({
});
const addIn: TMergeFilters = (x, y) => ({
- op: 'and',
content: y.content
.reduce((acc, curr) => {
const found = acc.find(a => compareTerms(a, curr));
@@ -95,6 +94,7 @@ const addIn: TMergeFilters = (x, y) => ({
].filter(removeNoValueFilter);
}, x.content)
.sort(sortFilters),
+ op: y.op || 'and',
});
const filterNoContent = (f: IGroupFilter) => (f.content.length ? f : null);
diff --git a/src/packages/@ncigdc/utils/withFacetSelection.js b/src/packages/@ncigdc/utils/withFacetSelection.js
index c7f5bc987..5fb4cb11a 100644
--- a/src/packages/@ncigdc/utils/withFacetSelection.js
+++ b/src/packages/@ncigdc/utils/withFacetSelection.js
@@ -2,7 +2,13 @@
/* eslint fp/no-this: 0, max-len: 1 */
import _ from 'lodash';
import { connect } from 'react-redux';
-import { withState, withProps, withHandlers, compose } from 'recompose';
+import {
+ compose,
+ setDisplayName,
+ withHandlers,
+ withProps,
+ withState,
+} from 'recompose';
import { add, remove, reset } from '@ncigdc/dux/customFacets';
import withRouter from '@ncigdc/utils/withRouter';
import { removeFilter } from '@ncigdc/utils/filters/index';
@@ -21,57 +27,63 @@ export default ({
presetFacetFields,
validFacetDocTypes,
validFacetPrefixes,
-}: TProps) =>
- compose(
- connect((state, props) => ({
- userSelectedFacets: state.customFacets[entityType],
- })),
- withState('shouldShowFacetSelection', 'setShouldShowFacetSelection', false),
- withProps(({ userSelectedFacets }) => ({
- facetExclusionTest: facet => {
- const facetFieldNamesToExclude = presetFacetFields.concat(
- userSelectedFacets.map(x => x.field),
- );
- const match = _.some([
- !_.includes(validFacetDocTypes, facet.doc_type),
- _.includes(facetFieldNamesToExclude, facet.field),
- validFacetPrefixes &&
+}: TProps) => compose(
+ setDisplayName('WithFacetSelection'),
+ connect((state, props) => ({
+ userSelectedFacets: state.customFacets[entityType],
+ })),
+ withState('shouldShowFacetSelection', 'setShouldShowFacetSelection', false),
+ withProps(({ userSelectedFacets }) => ({
+ facetExclusionTest: facet => {
+ const facetFieldNamesToExclude = presetFacetFields.concat(
+ userSelectedFacets.map(x => x.field),
+ );
+ const match = _.some([
+ !_.includes(validFacetDocTypes, facet.doc_type),
+ _.includes(facetFieldNamesToExclude, facet.field),
+ validFacetPrefixes &&
!_.includes(validFacetPrefixes.map(p => facet.full.indexOf(p)), 0),
- ]);
- return match;
- },
- })),
- withRouter,
- withHandlers({
- handleSelectFacet: ({
- userSelectedFacets,
- setShouldShowFacetSelection,
- dispatch,
- }) => facet => {
- setShouldShowFacetSelection(false);
- dispatch(add({ entityType, facet }));
- },
- handleResetFacets: ({ dispatch }) => () => {
- dispatch(reset({ entityType }));
- },
- handleRequestRemoveFacet: ({
- userSelectedFacets,
- dispatch,
- push,
- query,
- }) => facet => {
- dispatch(remove({ entityType, field: facet.field }));
- const newFilters = removeFilter(
- facet.full,
- parseFilterParam(query.filters),
- );
+ ]);
+ return match;
+ },
+ })),
+ withRouter,
+ withHandlers({
+ handleSelectFacet: ({
+ dispatch,
+ setShouldShowFacetSelection,
+ userSelectedFacets,
+ }) => facet => {
+ setShouldShowFacetSelection(false);
+ dispatch(add({
+ entityType,
+ facet,
+ }));
+ },
+ handleResetFacets: ({ dispatch }) => () => {
+ dispatch(reset({ entityType }));
+ },
+ handleRequestRemoveFacet: ({
+ dispatch,
+ push,
+ query,
+ userSelectedFacets,
+ }) => facet => {
+ dispatch(remove({
+ entityType,
+ field: facet.field,
+ }));
+ const newFilters = removeFilter(
+ facet.full,
+ parseFilterParam(query.filters),
+ );
- return push({
- query: removeEmptyKeys({
- ...query,
- filters: newFilters && stringifyJSONParam(newFilters),
- }),
- });
- },
- }),
- );
+ return push({
+ query: removeEmptyKeys({
+ ...query,
+ filters: newFilters && stringifyJSONParam(newFilters),
+ }),
+ });
+ },
+ }),
+);
diff --git a/src/packages/@ncigdc/utils/wrapSvg.js b/src/packages/@ncigdc/utils/wrapSvg.js
index 60b28f5f5..5d4f70d74 100644
--- a/src/packages/@ncigdc/utils/wrapSvg.js
+++ b/src/packages/@ncigdc/utils/wrapSvg.js
@@ -67,7 +67,7 @@ function buildForeignObject({
}): { html: string, height: number } {
const foreignObjects = elements.filter(Boolean);
// $FlowIgnore
- const elementsHeight = sum(foreignObjects.map(e => e.offsetHeight || 15));
+ const elementsHeight = sum(foreignObjects.map(e => (e.className === 'p-value' ? 25 : e.offsetHeight || 17)));
return {
height: elementsHeight,
@@ -175,10 +175,10 @@ const wrapSvg = ({
22,
])}"
viewBox="0 0 ${width} ${sum([
- height,
- afterObject.height,
- 22,
-])}"
+ height,
+ afterObject.height,
+ 22,
+ ])}"
style="font-size: 10px"
class="${EXPORT_CLASS} ${svgClass || ''} ${className}"
>
@@ -191,14 +191,14 @@ const wrapSvg = ({
${beforeObject.html}
${rightObject.html}
+ titleHeight,
+ margins.top,
+ beforeObject.height,
+ ])})">
${svg.innerHTML.replace(
- /url\(['"]?https?:\/\/[^#]+(#.+)['"]?\)/g,
- 'url($1)',
- )}
+ /url\(['"]?https?:\/\/[^#]+(#.+)['"]?\)/g,
+ 'url($1)',
+ )}
${afterObject.html}
${legends
diff --git a/tslint.json b/tslint.json
index 5d5e0f0e3..405081b63 100644
--- a/tslint.json
+++ b/tslint.json
@@ -11,7 +11,8 @@
"coverage/lcov-report/*.js",
"__mocks__/*.js",
"data/*.js",
- "public/*.js"
+ "public/*.js",
+ "src/packages/@oncojs/**/*"
]
},
"rules": {
@@ -77,4 +78,4 @@
"allow-snake-case"
]
}
-}
\ No newline at end of file
+}