From 1afb595888befb77af09a8dfb6c15714c194dc31 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Mon, 13 May 2019 12:54:24 -0400 Subject: [PATCH 01/22] Part of implement. --- package-lock.json | 362 +++++++----- package.json | 2 + .../components/Modals/GroupValuesModal.tsx | 157 +++++ .../analysis/defaultCDAVEvariables.js | 10 +- src/packages/@ncigdc/dux/analysis.ts | 6 +- .../ClinicalAnalysisResult.js | 20 +- .../ClinicalAnalysis/ClinicalVariableCard.js | 549 ++++++++++-------- 7 files changed, 702 insertions(+), 404 deletions(-) create mode 100644 src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx diff --git a/package-lock.json b/package-lock.json index 4c0b19ef6..a41dde728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -351,7 +351,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 +366,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 +430,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", @@ -1054,7 +1062,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", @@ -1338,7 +1346,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": { @@ -2390,7 +2398,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": { @@ -3263,7 +3271,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 +3284,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": { @@ -3342,7 +3350,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", @@ -4152,7 +4160,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 +4208,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", @@ -5211,7 +5227,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 +5273,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 +5285,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 +5296,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 +5309,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 +5412,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 +5908,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": { @@ -6173,8 +6189,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 +6200,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 +6231,7 @@ "bundled": true, "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -6228,26 +6240,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", @@ -6257,13 +6259,11 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "debug": { "version": "2.6.9", @@ -6271,13 +6271,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 +6305,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -6319,14 +6319,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 +6364,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 +6380,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "^2.1.0" } }, "ignore-walk": { @@ -6391,7 +6389,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -6399,14 +6397,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,38 +6414,34 @@ "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": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "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,14 +6450,13 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6479,9 +6471,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 +6482,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 +6500,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -6524,8 +6516,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 +6526,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 +6547,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.0", @@ -6567,7 +6558,7 @@ "bundled": true, "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "osenv": { @@ -6576,8 +6567,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 +6582,7 @@ }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, - "optional": true + "bundled": true }, "rc": { "version": "1.2.7", @@ -6600,10 +6590,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 +6615,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 +6630,7 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" }, "dependencies": { "glob": { @@ -6698,24 +6688,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 +6728,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", @@ -6762,14 +6750,12 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6802,7 +6788,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 +6927,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 +7297,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": { @@ -8527,7 +8513,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 }, @@ -13355,8 +13341,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 +13398,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", @@ -13842,7 +13890,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": { @@ -15064,7 +15112,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": { @@ -16177,7 +16225,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 +16237,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 +16248,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 +16277,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 +16288,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 +16301,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 +16323,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 +16337,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 +16385,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": { @@ -16782,7 +16830,7 @@ }, "json5": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { @@ -17994,7 +18042,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": { @@ -18575,7 +18623,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 } diff --git a/package.json b/package.json index 414b901aa..074cb9277 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,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 +81,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", diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx new file mode 100644 index 000000000..48a36952b --- /dev/null +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -0,0 +1,157 @@ +// @flow + +import React from 'react'; +import { compose, withState } from 'recompose'; +import { map, groupBy } from 'lodash'; +import { Row, Column } from '@ncigdc/uikit/Flex'; +import Button from '@ncigdc/uikit/Button'; +import { visualizingButton } from '@ncigdc/theme/mixins'; +// import { +// removeClinicalAnalysisVariable, +// updateClinicalAnalysisVariable, +// } from '@ncigdc/dux/analysis'; +import OutsideClickHandler from 'react-outside-click-handler'; + +const styles = { + button: { + ...visualizingButton, + minWidth: 100, + }, + horizonalPadding: { + paddingLeft: 20, + paddingRight: 20, + }, +}; + +// type TOption = { +// name: string, +// }; + +// type TProps = { +// options: Array, +// onClose: Function, +// selectedOptions: Array, +// setSelectedOptions: Function, +// }; + +const blockStyle = { + height: '500px', + margin: '20px', + padding: '20px', + width: '40%', +}; + +const listStyle = { + borderRadius: '2px', + borderStyle: 'inset', + borderWidth: '2px', + height: '100%', +}; + +export default compose( + withState('currentBins', 'setCurrentBins', ({ bins }: any) => bins.map((bin: any) => ({ + groupName: bin.groupName, + key: bin.key, + }))), + withState('selectedHidingBins', 'setSelectedHidingBins', {}), + withState('selectedGroupBins', 'setSelectedGroupBins', {}) +)( + ({ + bins, + currentBins, + setCurrentBins, + onUpdate, + onClose, + selectedHidingBins, + setSelectedHidingBins, + selectedGroupBins, + setSelectedGroupBins, + }: any) => ( + + {console.log('rawQueryDatavariable', bins)} +

Create Custom Bins: Something

+

+ Organize values into groups of your choosing. Click Save Bins to udpate + the analysis plots. +

+ console.log('out of there.')}> + + + + Hiding Values + + {currentBins + .filter((bin: any) => bin.groupName === '') + .map((bin: any) => ( + + + {bin.key} + + ))} + + + + + + + + Display Values + + {map( + groupBy( + currentBins.filter((bin: any) => bin.groupName !== ''), + 'groupName' + ), + group => ( + + {group[0].groupName} + + {group.length > 1 || group[0].key !== group[0].groupName + ? group.map(bin => ( + + {bin.key} + + )) + : null} + + ) + )} + + + + + + + + +
+ ) +); diff --git a/src/packages/@ncigdc/components/analysis/defaultCDAVEvariables.js b/src/packages/@ncigdc/components/analysis/defaultCDAVEvariables.js index 8d98ab6bd..2f92c21a2 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,7 +34,7 @@ export default { active_calculation: 'number', active_survival: 'overall', plotTypes: 'continuous', - bins: [], + bins: {}, scrollToCard: false, }, @@ -44,7 +44,7 @@ export default { active_calculation: 'number', active_survival: 'overall', plotTypes: 'continuous', - bins: [], + bins: {}, scrollToCard: false, }, 'diagnoses.primary_diagnosis': { @@ -53,7 +53,7 @@ export default { active_calculation: 'number', active_survival: 'overall', plotTypes: 'categorical', - bins: [], + bins: {}, scrollToCard: false, }, }; 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/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js index 11a09b912..42edb5ee8 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalAnalysisResult.js @@ -103,8 +103,8 @@ const CopyAnalysisModal = compose( withState( 'modalInputValue', 'setModalInputValue', - ({ analysis }) => `${analysis.name} copy` - ) + ({ analysis }) => `${analysis.name} copy`, + ), )(({ analysis, modalInputValue, setModalInputValue, dispatch, push }) => { return ( { push({ query: { @@ -172,7 +172,7 @@ const enhance = compose( }) => ({ parsedFacets: facets ? tryParseJSON(facets) : {}, hits, - }) + }), ), withProps( ({ @@ -205,20 +205,20 @@ const enhance = compose( setSurvivalPlotLoading(false); }, - }) + }), ), withPropsOnChange( ['currentAnalysis'], ({ currentAnalysis, populateSurvivalData }) => { populateSurvivalData(); - } + }, ), withHandlers({ handleQueryInputChange: ({ setSearchValue }) => (event: any) => setSearchValue(event.target.value), }), withTheme, - withRouter + withRouter, ); const ClinicalAnalysisResult = ({ @@ -285,7 +285,7 @@ const ClinicalAnalysisResult = ({ value: _.trim(value), property: 'name', id, - }) + }), ) } iconStyle={{ @@ -313,8 +313,8 @@ const ClinicalAnalysisResult = ({ analysis={currentAnalysis} dispatch={dispatch} push={push} - /> - ) + />, + ), ); }} leftIcon={} diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js index e58c69811..03e545de3 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard.js @@ -33,8 +33,11 @@ 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 { + CreateExploreCaseSetButton, + AppendExploreCaseSetButton, + RemoveFromExploreCaseSetButton, +} from '@ncigdc/modern_components/withSetAction'; import { setModal } from '@ncigdc/dux/modal'; import SaveSetModal from '@ncigdc/components/Modals/SaveSetModal'; @@ -46,9 +49,7 @@ import './survivalPlot.css'; import { downloadToTSV } from '@ncigdc/components/DownloadTableToTsvButton'; // survival plot -import { - getSurvivalCurvesArray, -} from '@ncigdc/utils/survivalplot'; +import { getSurvivalCurvesArray } from '@ncigdc/utils/survivalplot'; import SurvivalPlotWrapper from '@ncigdc/components/SurvivalPlotWrapper'; import { SpinnerIcon, @@ -70,6 +71,7 @@ import timestamp from '@ncigdc/utils/timestamp'; import { IS_CDAVE_DEV } from '@ncigdc/utils/constants'; import { MAXIMUM_CURVES, MINIMUM_CASES } from '../../utils/survivalplot'; +import GroupValuesModal from '@ncigdc/components/Modals/GroupValuesModal'; const colors = scaleOrdinal(schemeCategory10); @@ -97,7 +99,7 @@ type TVariableType = | 'Molecular_test'; // confirm type name interface IVariable { - bins: any[]; // tbd - bins still need spec + bins: any; // tbd - bins still need spec plotTypes: TPlotType; active_chart: TActiveChart; active_calculation: TActiveCalculation; @@ -119,7 +121,7 @@ interface IVizButton { title: string; icon: JSX.Element; action: ( - payload: IAnalysisPayload + payload: IAnalysisPayload, ) => { type: string, payload: IAnalysisPayload }; } @@ -139,29 +141,38 @@ const vizButtons: IVizButtons = { }, histogram: { title: 'Histogram', - icon: , + icon: ( + + ), action: updateClinicalAnalysisVariable, }, box: { title: 'Box/QQ Plot', - icon: , + icon: ( + + ), action: updateClinicalAnalysisVariable, }, delete: { title: 'Remove Card', - icon: , + icon: ( + + ), action: removeClinicalAnalysisVariable, }, }; @@ -205,11 +216,12 @@ const getRangeValue = (key, field, nextInterval) => { if (valueIsDays(field)) { return `${getLowerAgeYears(key)}${ nextInterval === 0 ? '+' : ` - ${getUpperAgeYears(nextInterval - 1)}` - } years`; - } if (valueIsYear(field)) { + } years`; + } + if (valueIsYear(field)) { return `${Math.floor(key)}${ nextInterval === 0 ? ' - present' : ` - ${nextInterval - 1}` - }`; + }`; } return key; }; @@ -221,7 +233,7 @@ const getCountLink = ({ doc_count, filters, totalDocs }) => ( searchTableTab: 'cases', filters, }} - > + > {(doc_count || 0).toLocaleString()} {` (${(((doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`} @@ -255,7 +267,6 @@ const ClinicalVariableCard: React.ComponentType = ({ if (isEmpty(rawData)) { return []; } - const displayData = type === 'continuous' ? rawData @@ -267,7 +278,9 @@ const ClinicalVariableCard: React.ComponentType = ({ .data.slice(0) .reverse() : rawData - .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing')) + .filter(bucket => + IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing', + ) .map(b => ({ ...b, key: b.key, @@ -319,9 +332,7 @@ const ClinicalVariableCard: React.ComponentType = ({ id={b.key} onChange={() => { if (find(selectedBuckets, { key: b.key })) { - setSelectedBuckets( - reject(selectedBuckets, r => r.key === b.key) - ); + setSelectedBuckets(reject(selectedBuckets, r => r.key === b.key)); } else { setSelectedBuckets([...selectedBuckets, b]); } @@ -332,7 +343,7 @@ const ClinicalVariableCard: React.ComponentType = ({ }} type="checkbox" value={b.key} - /> + /> ), ...(variable.active_chart === 'survival' ? { @@ -347,7 +358,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ? `Click icon to plot ${b.key}` : `Maximum plots (${MAXIMUM_CURVES}) reached` } - > + > ), @@ -475,21 +486,23 @@ const ClinicalVariableCard: React.ComponentType = ({ textAlign: 'right', }, }, - ...(chartType === 'survival' ? [ - { - key: 'survival', - title: 'Survival', - style: { - display: 'flex', - justifyContent: 'flex-end', - }, - 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', + }, }, - }, - ] : []), + ] + : []), ]; }; @@ -613,64 +626,62 @@ const ClinicalVariableCard: React.ComponentType = ({ padding: '0.5rem 1rem 1rem', ...style, }} - > + > +

-

{humanify({ term: fieldName })}

- {plots.concat('delete') - .map(plotType => ( - - - - ))} + {plots.concat('delete').map(plotType => ( + + + + ))}
- {isEmpty(tableData) - ? ( - - There is no data for this facet - - ) - : ( + {isEmpty(tableData) ? ( + + There is no data for this facet + + ) : (
{variable.active_chart !== 'survival' && ( @@ -681,49 +692,51 @@ const ClinicalVariableCard: React.ComponentType = ({ marginRight: 10, fontSize: '1.2rem', }} - > + > dispatch( - updateClinicalAnalysisVariable({ - fieldName, - variableKey: 'active_calculation', - value: 'percentage', - id, - }) - ) + onChange={() => + dispatch( + updateClinicalAnalysisVariable({ + fieldName, + variableKey: 'active_calculation', + value: 'percentage', + id, + }), + ) } style={{ marginRight: 5 }} type="radio" value="percentage" - /> + /> % of Cases - + + ({ label: d.fullLabel, @@ -739,13 +752,14 @@ const ClinicalVariableCard: React.ComponentType = ({ float: 'right', marginRight: 2, }} - svg={() => wrapSvg({ - selector: `#${wrapperId} svg`, - title: humanify({ term: fieldName }), - }) + svg={() => + wrapSvg({ + selector: `#${wrapperId} svg`, + title: humanify({ term: fieldName }), + }) } tooltipHTML="Download image or data" - /> + /> )} @@ -832,10 +846,10 @@ const ClinicalVariableCard: React.ComponentType = ({ yAxis={{ title: `${ variable.active_calculation === 'number' ? '#' : '%' - } of Cases`, + } of Cases`, style: styles.histogram(theme).axis, }} - /> + /> )} {variable.active_chart === 'survival' && (
= ({ height: '265px', margin: '5px 2px 10px', }} - > + > {selectedSurvivalValues.length === 0 ? ( = ({ plotType="clinicalOverall" survivalPlotLoading={survivalPlotLoading} uniqueClass="clinical-survival-plot" - /> + /> ) : ( - - )} + /> + )}
)} {variable.active_chart === 'box' && ( @@ -877,45 +891,47 @@ const ClinicalVariableCard: React.ComponentType = ({ backgroundColor: theme.greyScale5, margin: '5px 2px 10px', }} - > + > {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`, - }) + onClick={() => + downloadToTSV({ + selector: `#analysis-${tsvSubstring}-table`, + filename: `analysis-${ + currentAnalysis.name + }-${tsvSubstring}.${timestamp()}.tsv`, + }) } style={styles.actionMenuItem} - > + > Export to TSV - + { dispatch( @@ -930,14 +946,14 @@ const ClinicalVariableCard: React.ComponentType = ({ title={`Save ${totalFromSelectedBuckets} Cases as New Set`} total={totalFromSelectedBuckets} type="case" - /> - ) + />, + ), ); }} style={styles.actionMenuItem} - > + > Save as new case set - + { dispatch( @@ -953,14 +969,14 @@ const ClinicalVariableCard: React.ComponentType = ({ title={`Add ${totalFromSelectedBuckets} Cases to Existing Set`} total={totalFromSelectedBuckets} type="case" - /> - ) + />, + ), ); }} style={styles.actionMenuItem} - > + > Add to existing case set - + { dispatch( @@ -971,24 +987,80 @@ const ClinicalVariableCard: React.ComponentType = ({ RemoveFromSetButton={RemoveFromExploreCaseSetButton} title={`Remove ${totalFromSelectedBuckets} Cases from Existing Set`} type="case" - /> - ) + />, + ), ); }} style={styles.actionMenuItem} - > + > Remove from existing case set - + - + } + dropdownStyle={{ + left: 0, + minWidth: 205, }} + > + + dispatch( + setModal( + dispatch( + updateClinicalAnalysisVariable({ + fieldName, + variableKey: 'bins', + value: newBins.map(r => ({ ...r, groupName: r.key })), + id, + }), + ) + } + onClose={() => dispatch(setModal(null))} + />, + ), + ) + } + style={styles.actionMenuItem} > - Customize Bins - + Edit Bins + + { + // dispatch( + // setModal( + // + // ) + // ); + // }} + style={styles.actionMenuItem} + > + Reset to Default + + = ({ height: 175, }} tableId={`analysis-${tsvSubstring}-table`} - /> + /> )} @@ -1013,6 +1085,7 @@ export default compose( withState('selectedSurvivalValues', 'setSelectedSurvivalValues', []), withState('selectedSurvivalLoadingIds', 'setSelectedSurvivalLoadingIds', []), withState('survivalPlotLoading', 'setSurvivalPlotLoading', true), + withState('selectedBuckets', 'setSelectedBuckets', []), withProps(({ variable, data, fieldName }) => ({ rawQueryData: variable.plotTypes === 'continuous' @@ -1026,32 +1099,32 @@ export default compose( : (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}`, - }, + 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}`], - }, + }, + { + op: '>=', + content: { + field: fieldName, + value: [ + `${valueIsYear(fieldName) ? Math.floor(key) : key}`, + ], }, - ...(acc.nextInterval !== 0 ? [ + }, + ...(acc.nextInterval !== 0 + ? [ { op: '<=', content: { @@ -1059,34 +1132,33 @@ export default compose( 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), - }, + 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), }, - ], - }; - }, - }) - ), - withState('selectedBuckets', 'setSelectedBuckets', []), + filters, + }, + ], + }; + }, + })), withProps( ({ setSurvivalPlotLoading, @@ -1114,7 +1186,9 @@ export default compose( .data.slice(0) .reverse() : rawQueryData - .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing')) + .filter(bucket => + IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing', + ) .map(b => ({ ...b, chart_doc_count: b.doc_count, @@ -1193,28 +1267,43 @@ export default compose( setSelectedSurvivalLoadingIds([]); }); }, - }) + }), ), withPropsOnChange( - (props, nextProps) => props.variable.active_chart !== nextProps.variable.active_chart || + (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([])), + // withPropsOnChange(['data'], + // ({ rawQueryData, dispatch, data, fieldName, id }) => { + // // console.log('rawQueryData2', rawQueryData, data); + + // }, + // ), lifecycle({ componentDidMount(): void { - const { - dispatch, variable, fieldName, id, - } = this.props; + const { dispatch, variable, fieldName, id, data, rawQueryData } = this.props; + dispatch( + updateClinicalAnalysisVariable({ + fieldName, + variableKey: 'bins', + value: rawQueryData.map(r => ({ ...r, groupName: r.key })), + id, + }), + ); if (variable.scrollToCard === false) return; - const offset = document.getElementById('header').getBoundingClientRect().bottom + 10; + const offset = + document.getElementById('header').getBoundingClientRect().bottom + 10; const wrapperId = makeWrapperId(fieldName); const $anchor = document.getElementById(wrapperId); - const offsetTop = $anchor.getBoundingClientRect().top + window.pageYOffset; + const offsetTop = + $anchor.getBoundingClientRect().top + window.pageYOffset; window.scroll({ top: offsetTop - offset, behavior: 'smooth', @@ -1225,8 +1314,10 @@ export default compose( variableKey: 'scrollToCard', value: false, id, - }) + }), ); + + }, - }) + }), )(ClinicalVariableCard); From 21065f64d430dfda760dcd412cfb2973e07c27f6 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Mon, 13 May 2019 16:33:38 -0400 Subject: [PATCH 02/22] Part of implement. --- .../components/Modals/GroupValuesModal.tsx | 155 ++++++++++++++---- src/packages/@ncigdc/uikit/EditableLabel.js | 144 ++++++++-------- 2 files changed, 196 insertions(+), 103 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 48a36952b..2bdd6ff29 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { compose, withState } from 'recompose'; -import { map, groupBy } from 'lodash'; +import { map, groupBy, reduce } from 'lodash'; import { Row, Column } from '@ncigdc/uikit/Flex'; import Button from '@ncigdc/uikit/Button'; import { visualizingButton } from '@ncigdc/theme/mixins'; @@ -11,6 +11,7 @@ import { visualizingButton } from '@ncigdc/theme/mixins'; // updateClinicalAnalysisVariable, // } from '@ncigdc/dux/analysis'; import OutsideClickHandler from 'react-outside-click-handler'; +import EditableLabel from '@ncigdc/uikit/EditableLabel'; const styles = { button: { @@ -49,10 +50,10 @@ const listStyle = { }; export default compose( - withState('currentBins', 'setCurrentBins', ({ bins }: any) => bins.map((bin: any) => ({ - groupName: bin.groupName, - key: bin.key, - }))), + withState('currentBins', 'setCurrentBins', ({ bins }: any) => bins.reduce((acc: any, bin: any) => ({ + ...acc, + [bin.key]: bin.groupName, + }), {})), withState('selectedHidingBins', 'setSelectedHidingBins', {}), withState('selectedGroupBins', 'setSelectedGroupBins', {}) )( @@ -68,42 +69,85 @@ export default compose( setSelectedGroupBins, }: any) => ( - {console.log('rawQueryDatavariable', bins)}

Create Custom Bins: Something

Organize values into groups of your choosing. Click Save Bins to udpate the analysis plots.

- console.log('out of there.')}> + { + setSelectedGroupBins({}); + setSelectedHidingBins({}); + }} + > Hiding Values - - {currentBins - .filter((bin: any) => bin.groupName === '') + + {Object.keys(currentBins) + .filter((bin: any) => currentBins[bin] === '') .map((bin: any) => ( - - - {bin.key} + { + if (Object.keys(selectedGroupBins).length > 0) { + setSelectedGroupBins({}); + } + setSelectedHidingBins({ + ...selectedHidingBins, + [bin]: !selectedHidingBins[bin], + }); + }} + style={{ + backgroundColor: selectedHidingBins[bin] ? '#d5f4e6' : '', + paddingLeft: '10px', + }} + > + {bin} ))} - + -
+ ) ); diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index 556f31939..dad771e87 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -50,7 +50,9 @@ export default compose( }, }), lifecycle({ - componentDidUpdate({ text, value, setValue, isEditing }): void { + componentDidUpdate({ + text, value, setValue, isEditing, + }): void { if (!isEditing && value !== text) { setValue(text); } @@ -70,82 +72,84 @@ export default compose( containerStyle = {}, disabled = false, disabledMessage = null, + pencilEditingOnly = false, }) => (
- {isEditing ? ( - - setValue(e.target.value)} - onKeyDown={e => { - if (e.key === 'Enter') { - toggleEditingAndSave(); - } else if (e.key === 'Escape') { - handleCancel(); - } + justifyContent: 'space-between', + alignItems: 'center', + ...containerStyle, }} - type="text" - autoFocus - onFocus={e => e.target.select()} - /> - MAX_SET_NAME_LENGTH - ? `Maximum name length ${MAX_SET_NAME_LENGTH}` - : null - } - > - - - - - ) : ( - - - {children} - setValue(e.target.value)} + onFocus={e => e.target.select()} + onKeyDown={e => { + if (e.key === 'Enter') { + toggleEditingAndSave(); + } else if (e.key === 'Escape') { + handleCancel(); + } + }} style={{ - fontSize: '0.9em', - paddingLeft: '5px', - alignSelf: 'center', - color: 'rgb(96, 111, 81)', - ...iconStyle, - cursor: disabled ? 'not-allowed' : 'pointer', + width: '300px', + borderRadius: '4px', + transition: 'all 0.2s ease', }} - /> + type="text" + value={value} + /> + MAX_SET_NAME_LENGTH + ? `Maximum name length ${MAX_SET_NAME_LENGTH}` + : null + } + > + + + - - )} -
+ ) : ( + + + {children} + + + + )} + ) ); From 3649b99b703cf8aa499f6d933a3f2db3092a05d6 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Tue, 14 May 2019 09:33:26 -0400 Subject: [PATCH 03/22] Added the GroupValuesModal and bins objects. --- .../components/Modals/GroupValuesModal.tsx | 307 ++++++++++-------- .../ClinicalAnalysis/ClinicalVariableCard.js | 291 +++++++++-------- src/packages/@ncigdc/uikit/EditableLabel.js | 6 +- 3 files changed, 317 insertions(+), 287 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 2bdd6ff29..b465582c1 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -6,11 +6,12 @@ import { map, groupBy, reduce } from 'lodash'; import { Row, Column } from '@ncigdc/uikit/Flex'; import Button from '@ncigdc/uikit/Button'; import { visualizingButton } from '@ncigdc/theme/mixins'; + // import { // removeClinicalAnalysisVariable, // updateClinicalAnalysisVariable, // } from '@ncigdc/dux/analysis'; -import OutsideClickHandler from 'react-outside-click-handler'; +// import OutsideClickHandler from 'react-outside-click-handler'; import EditableLabel from '@ncigdc/uikit/EditableLabel'; const styles = { @@ -47,184 +48,200 @@ const listStyle = { borderStyle: 'inset', borderWidth: '2px', height: '100%', + overflow: 'scroll', }; export default compose( - withState('currentBins', 'setCurrentBins', ({ bins }: any) => bins.reduce((acc: any, bin: any) => ({ - ...acc, - [bin.key]: bin.groupName, - }), {})), + withState('currentBins', 'setCurrentBins', ({ bins }: any) => bins), withState('selectedHidingBins', 'setSelectedHidingBins', {}), withState('selectedGroupBins', 'setSelectedGroupBins', {}) )( ({ - bins, currentBins, setCurrentBins, onUpdate, onClose, + fieldName, selectedHidingBins, setSelectedHidingBins, selectedGroupBins, setSelectedGroupBins, }: any) => ( -

Create Custom Bins: Something

+

+ Create Custom Bins: + {' '} + {fieldName} +

Organize values into groups of your choosing. Click Save Bins to udpate the analysis plots.

- { setSelectedGroupBins({}); setSelectedHidingBins({}); }} - > + > */} - - - Hiding Values - - {Object.keys(currentBins) - .filter((bin: any) => currentBins[bin] === '') - .map((bin: any) => ( + + + Hiding Values + + {Object.keys(currentBins) + .filter((bin: any) => currentBins[bin].groupName === '') + .map((bin: any) => ( + { + if (Object.keys(selectedGroupBins).length > 0) { + setSelectedGroupBins({}); + } + setSelectedHidingBins({ + ...selectedHidingBins, + [bin]: !selectedHidingBins[bin], + }); + }} + style={{ + backgroundColor: selectedHidingBins[bin] ? '#d5f4e6' : '', + paddingLeft: '10px', + }} + > + {bin} + + ))} + + + + + + + + Display Values + + {map( + groupBy( + Object.keys(currentBins) + .filter((bin: any) => currentBins[bin].groupName !== ''), + key => currentBins[key].groupName + ), + (group: any) => ( + { - if (Object.keys(selectedGroupBins).length > 0) { - setSelectedGroupBins({}); + if (Object.keys(selectedHidingBins).length > 0) { + setSelectedHidingBins({}); } - setSelectedHidingBins({ - ...selectedHidingBins, - [bin]: !selectedHidingBins[bin], + setSelectedGroupBins({ + ...selectedGroupBins, + ...group.reduce((acc: any, bin: any) => ({ + ...acc, + [bin]: !group.every((gbin: any) => selectedGroupBins[gbin]), + }), {}), }); }} - style={{ - backgroundColor: selectedHidingBins[bin] ? '#d5f4e6' : '', - paddingLeft: '10px', - }} + style={{ backgroundColor: group.every((bin: any) => selectedGroupBins[bin]) ? '#d5f4e6' : '' }} > - {bin} - - ))} - - - - - - - - Display Values - - {map( - groupBy( - Object.keys(currentBins) - .filter((bin: any) => currentBins[bin] !== ''), - key => currentBins[key] - ), - (group: any) => ( - - { - if (Object.keys(selectedHidingBins).length > 0) { - setSelectedHidingBins({}); - } - setSelectedGroupBins({ - ...selectedGroupBins, - ...group.reduce((acc: any, bin: any) => ({ - ...acc, - [bin]: !group.every((gbin: any) => selectedGroupBins[gbin]), - }), {}), - }); + setCurrentBins({ + ...currentBins, + ...group.reduce((acc: any, bin: any) => ({ + ...acc, + [bin]: { + ...currentBins[bin], + groupName: value, + }, + }), {}), + }) + } + iconStyle={{ + cursor: 'pointer', + fontSize: '1.8rem', + marginLeft: 10, }} - style={{ backgroundColor: group.every((bin: any) => selectedGroupBins[bin]) ? '#d5f4e6' : '' }} + pencilEditingOnly + text={currentBins[group[0]].groupName} > - setCurrentBins({ - ...currentBins, - ...group.reduce((acc: any, bin: any) => ({ - ...acc, - [bin]: value, - }), {}), - }) - } - iconStyle={{ - cursor: 'pointer', - fontSize: '1.8rem', - marginLeft: 10, + {currentBins[group[0]].groupName} + + + {group.length > 1 || group[0] !== currentBins[group[0]].groupName + ? group.map((bin: any) => ( + setSelectedGroupBins({ + ...selectedGroupBins, + [bin]: !selectedGroupBins[bin], + })} + style={{ + backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '', + paddingLeft: '10px', }} - pencilEditingOnly - text={currentBins[group[0]]} > - {currentBins[group[0]]} - - - {group.length > 1 || group[0] !== currentBins[group[0]] - ? group.map((bin: any) => ( - setSelectedGroupBins({ - ...selectedGroupBins, - [bin]: !selectedGroupBins[bin], - })} - style={{ - backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '', - paddingLeft: '10px', - }} - > - {bin} - - )) - : null} - - ) - )} - + {bin} + + )) + : null} + + ) + )} - - +
+
+ {/* */} - @@ -412,11 +413,15 @@ const ClinicalVariableCard: React.ComponentType = ({ }; }); }; - + const binData = map(groupBy(variable.bins, bin => bin.groupName), (values, key) => ({ + key, + doc_count: values.reduce((acc, value) => acc + value.doc_count, 0), + })); + console.log('bindata', binData, rawQueryData); const tableData = variable.active_chart === 'box' ? getBoxTableData([]) - : getCategoricalTableData(rawQueryData, variable.plotTypes); + : getCategoricalTableData(binData, variable.plotTypes); const noDataTotal = totalDocs - rawQueryData.reduce((acc, bucket) => acc + bucket.doc_count, 0); @@ -626,21 +631,21 @@ const ClinicalVariableCard: React.ComponentType = ({ padding: '0.5rem 1rem 1rem', ...style, }} - > + > + >

+ > {humanify({ term: fieldName })}

@@ -663,7 +668,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : styles.common(theme)), margin: 2, }} - > + > {vizButtons[plotType].title} {vizButtons[plotType].icon} @@ -678,11 +683,11 @@ const ClinicalVariableCard: React.ComponentType = ({ justifyContent: 'center', alignItems: 'center', }} - > + > There is no data for this facet ) : ( -
+
{variable.active_chart !== 'survival' && (
@@ -692,51 +697,49 @@ const ClinicalVariableCard: React.ComponentType = ({ marginRight: 10, fontSize: '1.2rem', }} - > + > - dispatch( - updateClinicalAnalysisVariable({ - fieldName, - variableKey: 'active_calculation', - value: 'percentage', - id, - }), - ) + onChange={() => dispatch( + updateClinicalAnalysisVariable({ + fieldName, + variableKey: 'active_calculation', + value: 'percentage', + id, + }), + ) } style={{ marginRight: 5 }} type="radio" value="percentage" - /> + /> % of Cases - + + ({ label: d.fullLabel, @@ -752,14 +755,13 @@ const ClinicalVariableCard: React.ComponentType = ({ float: 'right', marginRight: 2, }} - svg={() => - wrapSvg({ - selector: `#${wrapperId} svg`, - title: humanify({ term: fieldName }), - }) + svg={() => wrapSvg({ + selector: `#${wrapperId} svg`, + title: humanify({ term: fieldName }), + }) } tooltipHTML="Download image or data" - /> + /> )} @@ -849,7 +851,7 @@ const ClinicalVariableCard: React.ComponentType = ({ } of Cases`, style: styles.histogram(theme).axis, }} - /> + /> )} {variable.active_chart === 'survival' && (
= ({ height: '265px', margin: '5px 2px 10px', }} - > + > {selectedSurvivalValues.length === 0 ? ( = ({ plotType="clinicalOverall" survivalPlotLoading={survivalPlotLoading} uniqueClass="clinical-survival-plot" - /> + /> ) : ( - + /> )}
)} @@ -891,7 +893,7 @@ const ClinicalVariableCard: React.ComponentType = ({ backgroundColor: theme.greyScale5, margin: '5px 2px 10px', }} - > + > {variable.active_chart}
)} @@ -901,37 +903,36 @@ const ClinicalVariableCard: React.ComponentType = ({ justifyContent: 'space-between', margin: '5px 0', }} - > + > } style={{ ...visualizingButton, padding: '0 12px', }} - > + > Select action - - } + + )} dropdownStyle={{ left: 0, minWidth: 205, }} - > + > - downloadToTSV({ - selector: `#analysis-${tsvSubstring}-table`, - filename: `analysis-${ - currentAnalysis.name - }-${tsvSubstring}.${timestamp()}.tsv`, - }) + onClick={() => downloadToTSV({ + selector: `#analysis-${tsvSubstring}-table`, + filename: `analysis-${ + currentAnalysis.name + }-${tsvSubstring}.${timestamp()}.tsv`, + }) } style={styles.actionMenuItem} - > + > Export to TSV - + { dispatch( @@ -946,14 +947,14 @@ const ClinicalVariableCard: React.ComponentType = ({ title={`Save ${totalFromSelectedBuckets} Cases as New Set`} total={totalFromSelectedBuckets} type="case" - />, + />, ), ); }} style={styles.actionMenuItem} - > + > Save as new case set - + { dispatch( @@ -969,14 +970,14 @@ const ClinicalVariableCard: React.ComponentType = ({ title={`Add ${totalFromSelectedBuckets} Cases to Existing Set`} total={totalFromSelectedBuckets} type="case" - />, + />, ), ); }} style={styles.actionMenuItem} - > + > Add to existing case set - + { dispatch( @@ -987,79 +988,82 @@ const ClinicalVariableCard: React.ComponentType = ({ RemoveFromSetButton={RemoveFromExploreCaseSetButton} title={`Remove ${totalFromSelectedBuckets} Cases from Existing Set`} type="case" - />, + />, ), ); }} style={styles.actionMenuItem} - > + > Remove from existing case set - + } style={{ ...visualizingButton, padding: '0 12px', }} - > + > Customize Bins - - } + + )} dropdownStyle={{ left: 0, minWidth: 205, }} - > + > - dispatch( - setModal( - dispatch( + onClick={() => dispatch( + setModal( + dispatch(setModal(null))} + onUpdate={ + (newBins) => { + dispatch( updateClinicalAnalysisVariable({ fieldName, - variableKey: 'bins', - value: newBins.map(r => ({ ...r, groupName: r.key })), id, + value: newBins, + variableKey: 'bins', }), - ) - } - onClose={() => dispatch(setModal(null))} + ); + dispatch(setModal(null)); + } + + } />, - ), - ) + ), + ) } style={styles.actionMenuItem} - > + > Edit Bins - + { - // dispatch( - // setModal( - // - // ) - // ); - // }} + onClick={() => { + dispatch( + updateClinicalAnalysisVariable({ + fieldName, + variableKey: 'bins', + value: rawQueryData.reduce((acc, r) => ({ + ...acc, + [r.key]: { + ...r, + groupName: r.key, + }, + }), {}), + id, + }), + ); + }} style={styles.actionMenuItem} - > + > Reset to Default - + @@ -1070,7 +1074,7 @@ const ClinicalVariableCard: React.ComponentType = ({ height: 175, }} tableId={`analysis-${tsvSubstring}-table`} - /> + />
)} @@ -1099,7 +1103,9 @@ export default compose( : (data || { buckets: [] }).buckets, totalDocs: (data.hits || { total: 0 }).total, })), - withProps(({ variable, fieldName, setId, totalDocs }) => ({ + withProps(({ + variable, fieldName, setId, totalDocs, + }) => ({ getBucketRangesAndFilters: (acc, { doc_count, key }) => { const filters = variable.plotTypes === 'categorical' @@ -1118,9 +1124,7 @@ export default compose( op: '>=', content: { field: fieldName, - value: [ - `${valueIsYear(fieldName) ? Math.floor(key) : key}`, - ], + value: [`${valueIsYear(fieldName) ? Math.floor(key) : key}`], }, }, ...(acc.nextInterval !== 0 @@ -1186,9 +1190,7 @@ export default compose( .data.slice(0) .reverse() : rawQueryData - .filter(bucket => - IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing', - ) + .filter(bucket => (IS_CDAVE_DEV ? bucket.key : bucket.key !== '_missing')) .map(b => ({ ...b, chart_doc_count: b.doc_count, @@ -1270,8 +1272,7 @@ export default compose( }), ), withPropsOnChange( - (props, nextProps) => - props.variable.active_chart !== nextProps.variable.active_chart || + (props, nextProps) => props.variable.active_chart !== nextProps.variable.active_chart || !isEqual(props.data, nextProps.data), ({ populateSurvivalData, variable }) => { if (variable.active_chart === 'survival') { @@ -1288,15 +1289,25 @@ export default compose( // ), lifecycle({ componentDidMount(): void { - const { dispatch, variable, fieldName, id, data, rawQueryData } = this.props; - dispatch( - updateClinicalAnalysisVariable({ - fieldName, - variableKey: 'bins', - value: rawQueryData.map(r => ({ ...r, groupName: r.key })), - id, - }), - ); + const { + dispatch, variable, fieldName, id, data, rawQueryData, + } = this.props; + if (Object.keys(variable.bins) === 0) { + dispatch( + updateClinicalAnalysisVariable({ + fieldName, + variableKey: 'bins', + value: rawQueryData.reduce((acc, r) => ({ + ...acc, + [r.key]: { + ...r, + groupName: r.key, + }, + }), {}), + id, + }), + ); + } if (variable.scrollToCard === false) return; const offset = document.getElementById('header').getBoundingClientRect().bottom + 10; @@ -1316,8 +1327,6 @@ export default compose( id, }), ); - - }, }), )(ClinicalVariableCard); diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index dad771e87..83899df10 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -132,12 +132,12 @@ export default compose( ) : ( {children} Date: Wed, 15 May 2019 12:18:34 -0400 Subject: [PATCH 04/22] Created function to update the group bins. --- .../components/Modals/GroupValuesModal.tsx | 127 ++++++++++-------- .../ClinicalAnalysis/ClinicalVariableCard.js | 15 +-- src/packages/@ncigdc/uikit/EditableLabel.js | 2 +- 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index b465582c1..29d0bec4e 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -1,17 +1,14 @@ // @flow - -import React from 'react'; +/* eslint-disable */ +import React, { ReactNode } from 'react'; +/* eslint-enable */ import { compose, withState } from 'recompose'; -import { map, groupBy, reduce } from 'lodash'; +import { + map, groupBy, reduce, max, +} from 'lodash'; import { Row, Column } from '@ncigdc/uikit/Flex'; import Button from '@ncigdc/uikit/Button'; import { visualizingButton } from '@ncigdc/theme/mixins'; - -// import { -// removeClinicalAnalysisVariable, -// updateClinicalAnalysisVariable, -// } from '@ncigdc/dux/analysis'; -// import OutsideClickHandler from 'react-outside-click-handler'; import EditableLabel from '@ncigdc/uikit/EditableLabel'; const styles = { @@ -29,12 +26,32 @@ const styles = { // name: string, // }; -// type TProps = { -// options: Array, -// onClose: Function, -// selectedOptions: Array, -// setSelectedOptions: Function, -// }; +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 { + currentBins: IBinsProps, + 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, +} const blockStyle = { height: '500px', @@ -52,9 +69,10 @@ const listStyle = { }; export default compose( - withState('currentBins', 'setCurrentBins', ({ bins }: any) => bins), + withState('currentBins', 'setCurrentBins', ({ bins }: { bins: IBinsProps }) => bins), withState('selectedHidingBins', 'setSelectedHidingBins', {}), - withState('selectedGroupBins', 'setSelectedGroupBins', {}) + withState('selectedGroupBins', 'setSelectedGroupBins', {}), + withState('editingGroupName', 'setEditingGroupName', '') )( ({ currentBins, @@ -66,8 +84,16 @@ export default compose( setSelectedHidingBins, selectedGroupBins, setSelectedGroupBins, - }: any) => ( - + editingGroupName, + setEditingGroupName, + }: IGroupValuesModalProps) => { + const groupNameMapping = groupBy( + Object.keys(currentBins) + .filter((bin: string) => currentBins[bin].groupName !== ''), + key => currentBins[key].groupName + ); + return ( +

Create Custom Bins: {' '} @@ -77,46 +103,35 @@ export default compose( Organize values into groups of your choosing. Click Save Bins to udpate the analysis plots.

- {/* { - setSelectedGroupBins({}); - setSelectedHidingBins({}); - }} - > */} - Hiding Values {Object.keys(currentBins) - .filter((bin: any) => currentBins[bin].groupName === '') - .map((bin: any) => ( + .filter((binKey: string) => currentBins[binKey].groupName === '') + .map((binKey: string) => ( { if (Object.keys(selectedGroupBins).length > 0) { setSelectedGroupBins({}); } setSelectedHidingBins({ ...selectedHidingBins, - [bin]: !selectedHidingBins[bin], + [binKey]: !selectedHidingBins[binKey], }); }} style={{ - backgroundColor: selectedHidingBins[bin] ? '#d5f4e6' : '', + backgroundColor: selectedHidingBins[binKey] ? '#d5f4e6' : '', paddingLeft: '10px', }} > - {bin} + {binKey} ))} - + - Display Values + > + Displayed Values - + {'Reset'} + + + + {map( @@ -285,7 +346,7 @@ export default compose( }); }} style={{ backgroundColor: group.every((binKey: string) => selectedGroupBins[binKey]) ? '#d5f4e6' : '' }} - > + > {group.length > 1 || group[0] !== groupName ? ( + > {groupName} - ) : currentBins[group[0]].key} + ) : currentBins[group[0]].key + ' (' + currentBins[group[0]].doc_count + ')'}
{group.length > 1 || group[0] !== groupName ? group.map((bin: string) => ( @@ -326,8 +387,8 @@ export default compose( backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '', paddingLeft: '10px', }} - > - {bin} + > + {bin + ' (' + currentBins[bin].doc_count + ')'}
)) : null} @@ -343,26 +404,26 @@ export default compose( justifyContent: 'flex-end', margin: '20px', }} - > + > 0 ? 'visible' : 'hidden', }} - > + > {'Warning: '} {warning}
diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index 23b586ba6..6c1c66b2c 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -150,7 +150,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Box/QQ Plot', }, delete: { @@ -161,7 +161,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Remove Card', }, histogram: { @@ -172,7 +172,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Histogram', }, survival: { @@ -237,7 +237,7 @@ const getCountLink = ({ doc_count, filters, totalDocs }) => ( filters, searchTableTab: 'cases', }} - > + > {(doc_count || 0).toLocaleString()} {` (${(((doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`} @@ -367,7 +367,7 @@ const ClinicalVariableCard: React.ComponentType = ({ }} type="checkbox" value={b.key} - /> + /> ), ...(variable.active_chart === 'survival' ? { @@ -382,7 +382,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ? `Click icon to plot ${b.key}` : `Maximum plots (${MAXIMUM_CURVES}) reached` } - > + > @@ -600,20 +600,20 @@ const ClinicalVariableCard: React.ComponentType = ({ padding: '0.5rem 1rem 1rem', ...style, }} - > + > + >

+ > {humanify({ term: fieldName })}

@@ -637,7 +637,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : styles.common(theme)), margin: 2, }} - > + > {vizButtons[plotType].title} {vizButtons[plotType].icon} @@ -654,7 +654,7 @@ const ClinicalVariableCard: React.ComponentType = ({ flex: 1, justifyContent: 'center', }} - > + > There is no data for this facet ) @@ -669,7 +669,7 @@ const ClinicalVariableCard: React.ComponentType = ({ fontSize: '1.2rem', marginRight: 10, }} - > + > = ({ style={{ marginRight: 5 }} type="radio" value="percentage" - /> + /> % of Cases = ({ }) } tooltipHTML="Download image or data" - /> + /> {/* {variable.active_chart === 'survival' && (
@@ -822,7 +822,7 @@ const ClinicalVariableCard: React.ComponentType = ({ variable.active_calculation === 'number' ? '#' : '%' } of Cases`, }} - /> + /> )} {variable.active_chart === 'survival' && (
= ({ justifyContent: 'center', margin: '5px 2px 10px', }} - > + > {selectedSurvivalValues.length === 0 ? ( = ({ plotType="clinicalOverall" survivalPlotLoading={survivalPlotLoading} uniqueClass="clinical-survival-plot" - /> + /> ) : ( - + /> )}
)} @@ -874,7 +874,7 @@ const ClinicalVariableCard: React.ComponentType = ({ justifyContent: 'space-between', margin: '5px 0', }} - > + > = ({ ...visualizingButton, padding: '0 12px', }} - > + > Select action )} @@ -891,7 +891,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > downloadToTSV({ filename: `analysis-${ @@ -901,7 +901,7 @@ const ClinicalVariableCard: React.ComponentType = ({ }) } style={styles.actionMenuItem} - > + > Export to TSV = ({ title={`Save ${totalFromSelectedBuckets} Cases as New Set`} total={totalFromSelectedBuckets} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Save as new case set = ({ title={`Add ${totalFromSelectedBuckets} Cases to Existing Set`} total={totalFromSelectedBuckets} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Add to existing case set = ({ RemoveFromSetButton={RemoveFromExploreCaseSetButton} title={`Remove ${totalFromSelectedBuckets} Cases from Existing Set`} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Remove from existing case set @@ -976,7 +976,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ...visualizingButton, padding: '0 12px', }} - > + > Customize Bins )} @@ -984,7 +984,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > dispatch( setModal( @@ -1004,12 +1004,12 @@ const ClinicalVariableCard: React.ComponentType = ({ dispatch(setModal(null)); } } - />, + />, ), ) } style={styles.actionMenuItem} - > + > Edit Bins = ({ ); }} style={styles.actionMenuItem} - > + > Reset to Default @@ -1044,7 +1044,7 @@ const ClinicalVariableCard: React.ComponentType = ({ height: 175, }} tableId={`analysis-${tsvSubstring}-table`} - /> + />
)} From 58bc263e5804a69f51a5d04ffedf5936968dfd0e Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Wed, 22 May 2019 16:03:51 -0400 Subject: [PATCH 07/22] Removed empty line. --- src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 3320c0938..41714ec0b 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -171,7 +171,6 @@ export default compose( }} > Hidden Values -
From b68193e7c976424a4bb1d20b55c243c491ea6f7a Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Thu, 23 May 2019 10:22:42 -0400 Subject: [PATCH 08/22] Removed bins with empty key from customBins. --- .../ClinicalAnalysis/ClinicalVariableCard/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index 216940122..1237688a0 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -258,6 +258,8 @@ const ClinicalVariableCard: React.ComponentType = ({ variable, wrapperId, }) => { + console.log('customBins', customBins); + const getBoxTableData = (data = {}) => ( Object.keys(data).length ? sortBy(Object.keys(data), datum => boxTableAllowedStats.indexOf(datum.toLowerCase())) @@ -1099,7 +1101,7 @@ export default compose( ? map(groupBy(variable.bins, bin => bin.groupName), (values, key) => ({ key, doc_count: values.reduce((acc, value) => acc + value.doc_count, 0), - })) + })).filter(bin => bin.key) : dataBuckets.map(b => ({ key: b.key, doc_count: b.doc_count, From 3267b6b4c23ed3303f0304ab8df51af18b3830eb Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Thu, 23 May 2019 10:23:14 -0400 Subject: [PATCH 09/22] Removed console. --- .../ClinicalAnalysis/ClinicalVariableCard/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index 1237688a0..a032a549a 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -258,7 +258,6 @@ const ClinicalVariableCard: React.ComponentType = ({ variable, wrapperId, }) => { - console.log('customBins', customBins); const getBoxTableData = (data = {}) => ( Object.keys(data).length From ade0bfdcd424952ae97e4b1deae7669244468883 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Fri, 24 May 2019 15:04:18 -0400 Subject: [PATCH 10/22] Improved onclick function of '<<' button --- src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 41714ec0b..daff5a9b5 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -230,8 +230,8 @@ export default compose( @@ -263,7 +259,7 @@ export default compose( alignItems: 'flex-end', display: 'flex', }} - > + > Displayed Values @@ -284,7 +280,7 @@ export default compose( setSelectedGroupBins({}); }} style={buttonStyle} - > + > {'Reset'} @@ -344,22 +340,34 @@ export default compose( }), {}), }); }} - style={{ backgroundColor: group.every((binKey: string) => selectedGroupBins[binKey]) ? '#d5f4e6' : '' }} - > + style={{ + backgroundColor: group.every((binKey: string) => selectedGroupBins[binKey]) ? '#d5f4e6' : '', + + + }} + > {group.length > 1 || group[0] !== groupName ? ( setCurrentBins({ - ...currentBins, - ...group.reduce((acc: ISelectedBinsProps, bin: string) => ({ - ...acc, - [bin]: { - ...currentBins[bin], - groupName: value, - }, - }), {}), - }) + containerStyle={{ + justifyContent: 'flex-start', + }} + handleSave={(value: string) => { + if (some(currentBins, (bin: IBinProps) => bin.groupName === value)) { + setWarning(`"${value}" has been existed.`); + return; + } + setCurrentBins({ + ...currentBins, + ...group.reduce((acc: ISelectedBinsProps, bin: string) => ({ + ...acc, + [bin]: { + ...currentBins[bin], + groupName: value, + }, + }), {}), + }); + } } iconStyle={{ cursor: 'pointer', @@ -367,30 +375,41 @@ export default compose( marginLeft: 10, }} isEditing={editingGroupName === groupName} + noEditingStyle={{ fontWeight: 'bold' }} pencilEditingOnly text={groupName} - > + > {groupName} - ) : currentBins[group[0]].key + ' (' + currentBins[group[0]].doc_count + ')'} + ) : ( +
+ {`${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' : '', - paddingLeft: '10px', - }} - > - {bin + ' (' + currentBins[bin].doc_count + ')'} - - )) - : null} + { + group.length > 1 || group[0] !== groupName + ? group.map((bin: string) => ( + setSelectedGroupBins({ + ...selectedGroupBins, + [bin]: !selectedGroupBins[bin], + })} + style={{ + backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '', + display: 'list-item', /* This has to be "list-item" */ + listStyleType: 'disc', /* See https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type */ + listStylePosition: 'inside', + paddingLeft: '5px', + }} + > + {`${bin} (${currentBins[bin].doc_count})`} + + )) + : null + } ) )} @@ -403,26 +422,26 @@ export default compose( justifyContent: 'flex-end', margin: '20px', }} - > + > 0 ? 'visible' : 'hidden', }} - > + > {'Warning: '} {warning} diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index df880e549..6ea2dd38d 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -20,7 +20,7 @@ export default compose( withState('isEditing', 'setIsEditing', ({ isEditing }) => isEditing || false), withState('value', 'setValue', ({ text }) => text), defaultProps({ - handleSave: value => console.log(value), + handleSave: () => { }, }), withHandlers({ handleCancel: ({ setIsEditing, setValue, text }) => () => { @@ -31,7 +31,6 @@ export default compose( withHandlers({ toggleEditingAndSave: ({ disabled = false, - handleCancel, handleSave, isEditing, setIsEditing, @@ -39,7 +38,7 @@ export default compose( value, }) => () => { if (disabled) { - return null; + return; } if (value.length !== 0) { setIsEditing(!isEditing); @@ -60,12 +59,10 @@ export default compose( }) )( ({ - text, isEditing, toggleEditingAndSave, value, setValue, - setIsEditing, handleCancel, children, iconStyle = {}, @@ -73,14 +70,16 @@ export default compose( disabled = false, disabledMessage = null, pencilEditingOnly = false, - }) => ( -
+ noEditingStyle, + }) => { + return ( + {isEditing ? ( @@ -96,9 +95,9 @@ export default compose( } }} style={{ - width: '300px', borderRadius: '4px', transition: 'all 0.2s ease', + width: '300px', }} type="text" value={value} @@ -129,20 +128,24 @@ export default compose( Cancel - ) : ( - + ) + : ( + {children} )} -
- ) + + ); + } ); diff --git a/src/packages/@ncigdc/uikit/Modal.js b/src/packages/@ncigdc/uikit/Modal.js index 9900e2fff..632ae4e55 100644 --- a/src/packages/@ncigdc/uikit/Modal.js +++ b/src/packages/@ncigdc/uikit/Modal.js @@ -26,19 +26,21 @@ 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" - > + contentLabel="Modal" + isOpen={isOpen} + onRequestClose={onRequestClose || (() => { })} + style={{ ..._.merge({}, modalStyles, style) }} + > {Children.map(children, child => cloneElement(child, { ...child.props }))} ); From 598cb9ed9124b24fc7d6534e370acaaa88f6bcad Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Mon, 3 Jun 2019 09:48:13 -0400 Subject: [PATCH 12/22] Updated the groupValuesModal. --- .../components/Modals/GroupValuesModal.tsx | 27 ++-- .../ClinicalVariableCard/index.js | 115 ++++++++---------- src/packages/@ncigdc/uikit/EditableLabel.js | 107 +++++++++------- 3 files changed, 131 insertions(+), 118 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 73f456426..8cdc0dc13 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -218,6 +218,7 @@ export default compose( }, {}), }); setSelectedHidingBins({}); + setWarning(''); }} style={{ margin: '10px' }} > @@ -247,6 +248,7 @@ export default compose( }, {}), }); setSelectedGroupBins({}); + setWarning(''); }} style={{ margin: '10px' }} > @@ -278,6 +280,7 @@ export default compose( }, {}), }); setSelectedGroupBins({}); + setWarning(''); }} style={buttonStyle} > @@ -305,6 +308,7 @@ export default compose( }, {}), }); setSelectedGroupBins({}); + setWarning(''); }} style={buttonStyle} > @@ -312,7 +316,10 @@ export default compose( @@ -416,7 +411,7 @@ const ClinicalVariableCard: React.ComponentType = ({ const tableData = variable.active_chart === 'box' ? getBoxTableData(dataValues) - : getCategoricalTableData(customBins, variable.plotTypes); + : getCategoricalTableData(customBins, variable.plotTypes, dataBuckets); const getHeadings = chartType => { return chartType === 'box' @@ -536,6 +531,8 @@ const ClinicalVariableCard: React.ComponentType = ({ }; const getCategoricalSetFilters = () => { + console.log('dataBu', variable.bins, selectedBuckets); + // (selectedBuckets|| []).map(bucket => bucket) const bucketFilters = [] .concat(selectedBuckets.filter(bucket => bucket.key !== '_missing').length > 0 && [ { @@ -576,7 +573,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ); }; - const cardFilters = selectedBuckets && selectedBuckets.length + const cardFilters = get(selectedBuckets, 'length', 0) ? variable.plotTypes === 'continuous' ? getContinuousSetFilters() : getCategoricalSetFilters() @@ -594,20 +591,20 @@ const ClinicalVariableCard: React.ComponentType = ({ padding: '0.5rem 1rem 1rem', ...style, }} - > + > + >

+ > {humanify({ term: fieldName })}

@@ -631,7 +628,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : styles.common(theme)), margin: 2, }} - > + > {vizButtons[plotType].title} {vizButtons[plotType].icon} @@ -648,7 +645,7 @@ const ClinicalVariableCard: React.ComponentType = ({ flex: 1, justifyContent: 'center', }} - > + > There is no data for this facet ) @@ -663,7 +660,7 @@ const ClinicalVariableCard: React.ComponentType = ({ fontSize: '1.2rem', marginRight: 10, }} - > + > = ({ style={{ marginRight: 5 }} type="radio" value="percentage" - /> + /> % of Cases = ({ }) } tooltipHTML="Download image or data" - /> + /> {/* {variable.active_chart === 'survival' && (
@@ -816,7 +813,7 @@ const ClinicalVariableCard: React.ComponentType = ({ variable.active_calculation === 'number' ? '#' : '%' } of Cases`, }} - /> + /> )} {variable.active_chart === 'survival' && (
= ({ justifyContent: 'center', margin: '5px 2px 10px', }} - > + > {selectedSurvivalValues.length === 0 ? ( = ({ plotType="clinicalOverall" survivalPlotLoading={survivalPlotLoading} uniqueClass="clinical-survival-plot" - /> + /> ) : ( - + /> )}
)} @@ -868,7 +865,7 @@ const ClinicalVariableCard: React.ComponentType = ({ justifyContent: 'space-between', margin: '5px 0', }} - > + > = ({ ...visualizingButton, padding: '0 12px', }} - > + > Select action )} @@ -885,7 +882,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > downloadToTSV({ filename: `analysis-${ @@ -895,7 +892,7 @@ const ClinicalVariableCard: React.ComponentType = ({ }) } style={styles.actionMenuItem} - > + > Export to TSV = ({ title={`Save ${totalFromSelectedBuckets} Cases as New Set`} total={totalFromSelectedBuckets} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Save as new case set = ({ title={`Add ${totalFromSelectedBuckets} Cases to Existing Set`} total={totalFromSelectedBuckets} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Add to existing case set = ({ selected={Object.keys(get(currentAnalysis, 'sets.case', {}))[0] || ''} title={`Remove ${totalFromSelectedBuckets} Cases from Existing Set`} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Remove from existing case set @@ -971,7 +968,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ...visualizingButton, padding: '0 12px', }} - > + > Customize Bins )} @@ -979,7 +976,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > dispatch( setModal( @@ -999,12 +996,12 @@ const ClinicalVariableCard: React.ComponentType = ({ dispatch(setModal(null)); } } - />, + />, ), ) } style={styles.actionMenuItem} - > + > Edit Bins = ({ ); }} style={styles.actionMenuItem} - > + > Reset to Default @@ -1039,7 +1036,7 @@ const ClinicalVariableCard: React.ComponentType = ({ height: 175, }} tableId={`analysis-${tsvSubstring}-table`} - /> + />
)} @@ -1057,20 +1054,11 @@ export default compose( withState('selectedBuckets', 'setSelectedBuckets', []), withProps(({ data, fieldName, variable }) => { const sanitisedId = fieldName.split('.').pop(); - const rawQueryData = (data.explore && data.explore.cases.aggregations - ? data.explore.cases.aggregations[fieldName.replace('.', '__')] - : data); + const rawQueryData = get(data, `explore.cases.aggregations.${fieldName.replace('.', '__')}`, data); const dataDimension = dataDimensions[sanitisedId] && dataDimensions[sanitisedId].unit; - return Object.assign( { - dataBuckets: rawQueryData - ? variable.plotTypes === 'continuous' - ? rawQueryData.histogram - ? rawQueryData.histogram.buckets - : [] - : rawQueryData.buckets - : [], + dataBuckets: get(rawQueryData, variable.plotTypes === 'continuous' ? 'histogram.buckets' : 'buckets', []), dataValues: variable.plotTypes === 'continuous' && map( { ...rawQueryData.stats, @@ -1094,8 +1082,7 @@ export default compose( ...acc, ...item, }), {}), - rawQueryData, - totalDocs: (data.hits || { total: 0 }).total, + totalDocs: get(data, 'hits.total', 0), wrapperId: `${sanitisedId}-chart`, }, dataDimension && { dataDimension }, @@ -1109,7 +1096,7 @@ export default compose( })).filter(bin => bin.key) : dataBuckets.map(b => ({ key: b.key, - doc_count: b.doc_count, + doc_count: b.doc_count, // TODO: Remove this groupName: b.key, })), })), diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index 6ea2dd38d..24c9f0f14 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -14,6 +14,7 @@ import { Row } from '@ncigdc/uikit/Flex'; import Button from '@ncigdc/uikit/Button'; import { visualizingButton } from '@ncigdc/theme/mixins'; import { MAX_SET_NAME_LENGTH } from '@ncigdc/utils/constants'; +import OutsideClickHandler from 'react-outside-click-handler'; import { Tooltip } from './Tooltip/index'; export default compose( @@ -71,63 +72,75 @@ export default compose( disabledMessage = null, pencilEditingOnly = false, noEditingStyle, + buttonDisplayed = true, + outsideClickHandlerDisabled = true, }) => { return ( {isEditing ? ( - toggleEditingAndSave()} > - setValue(e.target.value)} - onFocus={e => e.target.select()} - onKeyDown={e => { - if (e.key === 'Enter') { - toggleEditingAndSave(); - } else if (e.key === 'Escape') { - handleCancel(); - } - }} + - MAX_SET_NAME_LENGTH - ? `Maximum name length ${MAX_SET_NAME_LENGTH}` - : null - } > - - - - + type="text" + value={value} + /> + {buttonDisplayed + ? ( + + MAX_SET_NAME_LENGTH + ? `Maximum name length ${MAX_SET_NAME_LENGTH}` + : null + } + > + + + + + ) : null} + + ) : ( From 20f1d02bc9d38f4199c4b58d2ea52f6813700724 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Mon, 3 Jun 2019 10:48:03 -0400 Subject: [PATCH 13/22] changed the warning message; added the numbers of data in hidden value list. --- .../@ncigdc/components/Modals/GroupValuesModal.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 8cdc0dc13..4cff7de0a 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -193,7 +193,7 @@ export default compose( paddingLeft: '10px', }} > - {binKey} + {`${binKey} (${currentBins[binKey].doc_count})`}
))} @@ -362,7 +362,7 @@ export default compose( }} handleSave={(value: string) => { if (some(currentBins, (bin: IBinProps) => bin.groupName.trim() === value.trim())) { - setWarning(`"${value.trim()}" has already existed.`); + setWarning(`"${value.trim()}" already exists.`); return; } setCurrentBins({ @@ -412,8 +412,8 @@ export default compose( } style={{ backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '', - display: 'list-item', /* This has to be "list-item" */ - listStyleType: 'disc', /* See https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type */ + display: 'list-item', + listStyleType: 'disc', listStylePosition: 'inside', paddingLeft: '5px', }} From 12e03fd083b00dea687ee7dc172b7149ab9db52c Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Mon, 3 Jun 2019 11:16:58 -0400 Subject: [PATCH 14/22] Added outsideClickHandlerDisabled to false in GroupValuesModal. --- src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 4cff7de0a..67486d752 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -385,6 +385,7 @@ export default compose( }} isEditing={editingGroupName === groupName} noEditingStyle={{ fontWeight: 'bold' }} + outsideClickHandlerDisabled={false} pencilEditingOnly text={groupName} > From a5c69d613993eda38df92b8e09baa3c0b14d0aef Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Mon, 3 Jun 2019 11:31:49 -0400 Subject: [PATCH 15/22] Sorted the binData before displaying it. --- .../ClinicalAnalysis/ClinicalVariableCard/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index f510eb36b..5335b0d79 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -273,7 +273,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : [] ); - const getCategoricalTableData = (binData, type, dataBuckets) => { + const getCategoricalTableData = (binData, type) => { if (isEmpty(binData)) { return []; } @@ -288,6 +288,7 @@ const ClinicalVariableCard: React.ComponentType = ({ .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, @@ -411,7 +412,7 @@ const ClinicalVariableCard: React.ComponentType = ({ const tableData = variable.active_chart === 'box' ? getBoxTableData(dataValues) - : getCategoricalTableData(customBins, variable.plotTypes, dataBuckets); + : getCategoricalTableData(customBins, variable.plotTypes); const getHeadings = chartType => { return chartType === 'box' From 726710fbfd3f17993cb56a6e55e92ece9259d6ae Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Tue, 4 Jun 2019 08:23:47 -0400 Subject: [PATCH 16/22] Updated bins when setId and totalDocs number changes. --- .eslintrc.js | 4 +- .../ClinicalVariableCard/index.js | 775 +++++++++--------- 2 files changed, 406 insertions(+), 373 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a27bcc179..0a32170a5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -44,7 +44,7 @@ module.exports = { 'arrow-body-style': 'off', 'arrow-parens': 'off', 'camelcase': ['warn', { - allow: ["^UNSAFE_"], + allow: ["^UNSAFE_", "doc_count"], }], 'comma-dangle': ['warn', 'always-multiline'], 'func-names': ['warn', 'as-needed'], @@ -176,7 +176,7 @@ 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' , { + '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 diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index 5335b0d79..908adc3c3 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -21,6 +21,7 @@ import { truncate, groupBy, get, + reduce, } from 'lodash'; import { scaleOrdinal, schemeCategory10 } from 'd3'; @@ -38,7 +39,6 @@ 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'; @@ -146,7 +146,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Box/QQ Plot', }, delete: { @@ -157,7 +157,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Remove Card', }, histogram: { @@ -168,7 +168,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Histogram', }, survival: { @@ -223,13 +223,323 @@ const getCountLink = ({ doc_count, filters, totalDocs }) => ( filters, searchTableTab: 'cases', }} - > + > {(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, + type, + getBucketRangesAndFilters, + fieldName, + totalDocs, + selectedSurvivalValues, + setId, + selectedBuckets, + setSelectedBuckets, + variable, + updateSelectedSurvivalValues, + selectedSurvivalLoadingIds, +) => { + if (isEmpty(binData)) { + return []; + } + const displayData = type === '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` + } + > + + + ), + } + : {}), + })); +}; + +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 ? `${dataDimension}s` : '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, @@ -257,220 +567,22 @@ const ClinicalVariableCard: React.ComponentType = ({ variable, wrapperId, }) => { - 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 getCategoricalTableData = (binData, type) => { - if (isEmpty(binData)) { - return []; - } - const displayData = type === '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.key], - }, - 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.key], - }, - ]), - 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` - } - > - - - ), - } - : {}), - })); - }; - const tableData = variable.active_chart === 'box' ? getBoxTableData(dataValues) - : getCategoricalTableData(customBins, variable.plotTypes); - - const getHeadings = chartType => { - return chartType === 'box' - ? [ - { - key: 'stat', - title: 'Statistics', - }, - { - key: 'count', - style: { textAlign: 'right' }, - title: `${dataDimension ? `${dataDimension}s` : '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', - }, - ] : []), - ]; - }; + : getCategoricalTableData( + customBins, + variable.plotTypes, + getBucketRangesAndFilters, + fieldName, + totalDocs, + selectedSurvivalValues, + setId, + selectedBuckets, + setSelectedBuckets, + variable, + updateSelectedSurvivalValues, + selectedSurvivalLoadingIds, + ); const chartData = variable.active_chart === 'histogram' @@ -492,96 +604,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ? 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, { - 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 getCategoricalSetFilters = () => { - console.log('dataBu', variable.bins, selectedBuckets); - // (selectedBuckets|| []).map(bucket => bucket) - const bucketFilters = [] - .concat(selectedBuckets.filter(bucket => bucket.key !== '_missing').length > 0 && [ - { - content: { - field: fieldName, - value: selectedBuckets - .filter(bucket => bucket.key !== '_missing') - .map(selectedBucket => selectedBucket.key), - }, - 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 cardFilters = get(selectedBuckets, 'length', 0) - ? variable.plotTypes === 'continuous' - ? getContinuousSetFilters() - : getCategoricalSetFilters() - : filters; - const tsvSubstring = fieldName.replace(/\./g, '-'); - return ( = ({ padding: '0.5rem 1rem 1rem', ...style, }} - > + > + >

+ > {humanify({ term: fieldName })}

@@ -629,7 +652,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : styles.common(theme)), margin: 2, }} - > + > {vizButtons[plotType].title} {vizButtons[plotType].icon} @@ -646,7 +669,7 @@ const ClinicalVariableCard: React.ComponentType = ({ flex: 1, justifyContent: 'center', }} - > + > There is no data for this facet ) @@ -661,7 +684,7 @@ const ClinicalVariableCard: React.ComponentType = ({ fontSize: '1.2rem', marginRight: 10, }} - > + > = ({ style={{ marginRight: 5 }} type="radio" value="percentage" - /> + /> % of Cases = ({ }) } tooltipHTML="Download image or data" - /> + /> {/* {variable.active_chart === 'survival' && (
@@ -814,7 +837,7 @@ const ClinicalVariableCard: React.ComponentType = ({ variable.active_calculation === 'number' ? '#' : '%' } of Cases`, }} - /> + /> )} {variable.active_chart === 'survival' && (
= ({ justifyContent: 'center', margin: '5px 2px 10px', }} - > + > {selectedSurvivalValues.length === 0 ? ( = ({ plotType="clinicalOverall" survivalPlotLoading={survivalPlotLoading} uniqueClass="clinical-survival-plot" - /> + /> ) : ( - + /> )}
)} @@ -866,7 +889,7 @@ const ClinicalVariableCard: React.ComponentType = ({ justifyContent: 'space-between', margin: '5px 0', }} - > + > = ({ ...visualizingButton, padding: '0 12px', }} - > + > Select action )} @@ -883,7 +906,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > downloadToTSV({ filename: `analysis-${ @@ -893,7 +916,7 @@ const ClinicalVariableCard: React.ComponentType = ({ }) } style={styles.actionMenuItem} - > + > Export to TSV = ({ + /> ) ); }} style={styles.actionMenuItem} - > + > Save as new case set = ({ AppendSetButton={AppendExploreCaseSetButton} displayType="case" field="cases.case_id" - filters={cardFilters} + filters={getCardFilters(variable.plotTypes, selectedBuckets, fieldName, filters)} scope="explore" score="gene.gene_id" sort={null} title={`Add ${totalFromSelectedBuckets} Cases to Existing Set`} total={totalFromSelectedBuckets} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Add to existing case set = ({ setModal( + /> ) ); }} style={styles.actionMenuItem} - > + > Remove from existing case set @@ -969,7 +992,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ...visualizingButton, padding: '0 12px', }} - > + > Customize Bins )} @@ -977,7 +1000,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > dispatch( setModal( @@ -997,12 +1020,12 @@ const ClinicalVariableCard: React.ComponentType = ({ dispatch(setModal(null)); } } - />, + />, ), ) } style={styles.actionMenuItem} - > + > Edit Bins = ({ ); }} style={styles.actionMenuItem} - > + > Reset to Default @@ -1032,12 +1055,12 @@ const ClinicalVariableCard: React.ComponentType = ({ + />
)}
@@ -1089,18 +1112,41 @@ export default compose( dataDimension && { dataDimension }, ); }), - withProps(({ dataBuckets, variable }) => ({ - customBins: Object.keys(variable.bins).length > 0 - ? map(groupBy(variable.bins, bin => bin.groupName), (values, key) => ({ - key, - doc_count: values.reduce((acc, value) => acc + value.doc_count, 0), - })).filter(bin => bin.key) - : dataBuckets.map(b => ({ - key: b.key, - doc_count: b.doc_count, // TODO: Remove this - groupName: b.key, - })), - })), + withPropsOnChange(['setId', 'totalDocs'], ({ + dataBuckets, + dispatch, + fieldName, + id, + variable, + }) => { + dispatch( + updateClinicalAnalysisVariable({ + fieldName, + id, + value: { + ...reduce(variable.bins, (acc, bin, key) => ({ + ...acc, + [key]: { + doc_count: 0, + groupName: bin.groupName, + key, + }, + }), {}), + ...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, @@ -1108,6 +1154,11 @@ export default compose( 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' @@ -1180,7 +1231,6 @@ export default compose( }) => ({ populateSurvivalData: () => { setSurvivalPlotLoading(true); - const dataForSurvival = variable.plotTypes === 'continuous' ? dataBuckets @@ -1286,29 +1336,12 @@ export default compose( lifecycle({ componentDidMount(): void { const { - dataBuckets, dispatch, fieldName, id, variable, wrapperId, } = this.props; - if (Object.keys(variable.bins).length === 0) { - dispatch( - updateClinicalAnalysisVariable({ - fieldName, - id, - value: dataBuckets.reduce((acc, r) => ({ - ...acc, - [r.key]: { - ...r, - groupName: r.key, - }, - }), {}), - variableKey: 'bins', - }), - ); - } if (variable.scrollToCard === false) return; const offset = document.getElementById('header').getBoundingClientRect().bottom + 10; const $anchor = document.getElementById(`${wrapperId}-container`); From dd2d8173907fed6e6de797f3ab9aed1ec0210257 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Tue, 4 Jun 2019 22:51:31 -0400 Subject: [PATCH 17/22] Added warning after each group name. Changed toggleEditingAndSave function in EditableLabel. --- .../components/Modals/GroupValuesModal.tsx | 154 ++++++++++-------- src/packages/@ncigdc/uikit/EditableLabel.js | 24 +-- 2 files changed, 103 insertions(+), 75 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 67486d752..378851b95 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -67,9 +67,10 @@ interface IGroupValuesModalProps { editingGroupName: string, setEditingGroupName: (editingGroupName: string) => void, children?: ReactNode, - warning: string, - setWarning: (warning: string) => void, - bins: any, + globalWarning: string, + setGlobalWarning: (globalWarning: string) => void, + setListWarning: any, + listWarning: any, } const blockStyle = { @@ -96,7 +97,8 @@ export default compose( withState('currentBins', 'setCurrentBins', ({ bins }: { bins: IBinsProps }) => bins), withState('selectedHidingBins', 'setSelectedHidingBins', {}), withState('selectedGroupBins', 'setSelectedGroupBins', {}), - withState('warning', 'setWarning', ''), + withState('globalWarning', 'setGlobalWarning', ''), + withState('listWarning', 'setListWarning', {}), withProps(({ currentBins, selectedGroupBins, @@ -130,7 +132,6 @@ export default compose( )( ({ binGrouping, - bins, currentBins, editingGroupName, fieldName, @@ -139,11 +140,12 @@ export default compose( selectedGroupBins, selectedHidingBins, setCurrentBins, - setEditingGroupName, setSelectedGroupBins, setSelectedHidingBins, - setWarning, - warning, + setGlobalWarning, + globalWarning, + setListWarning, + listWarning, }: IGroupValuesModalProps) => { const groupNameMapping = groupBy( Object.keys(currentBins) @@ -169,7 +171,7 @@ export default compose( alignItems: 'flex-end', display: 'flex', }} - > + > Hidden Values @@ -192,7 +194,7 @@ export default compose( backgroundColor: selectedHidingBins[binKey] ? '#d5f4e6' : '', paddingLeft: '10px', }} - > + > {`${binKey} (${currentBins[binKey].doc_count})`} ))} @@ -218,10 +220,11 @@ export default compose( }, {}), }); setSelectedHidingBins({}); - setWarning(''); + setGlobalWarning(''); + setListWarning({}); }} style={{ margin: '10px' }} - > + > {'>>'} @@ -261,7 +265,7 @@ export default compose( alignItems: 'flex-end', display: 'flex', }} - > + > Displayed Values @@ -280,10 +284,11 @@ export default compose( }, {}), }); setSelectedGroupBins({}); - setWarning(''); + setGlobalWarning(''); + setListWarning({}); }} style={buttonStyle} - > + > {'Reset'} @@ -352,45 +360,63 @@ export default compose( }} - > + > {group.length > 1 || group[0] !== groupName ? ( - { - if (some(currentBins, (bin: IBinProps) => bin.groupName.trim() === value.trim())) { - setWarning(`"${value.trim()}" already exists.`); - return; + + { + setListWarning({}); + if ( + some(currentBins, (bin: IBinProps) => bin.groupName.trim() === value.trim()) && + groupName.trim() !== value.trim() + ) { + setListWarning({ + ...listWarning, + [groupName]: `"${value.trim()}" already exists.`, + }); + return 'unsave'; + } + setCurrentBins({ + ...currentBins, + ...group.reduce((acc: ISelectedBinsProps, bin: string) => ({ + ...acc, + [bin]: { + ...currentBins[bin], + groupName: value, + }, + }), {}), + }); + setGlobalWarning(''); + setListWarning({}); + setSelectedGroupBins({}); + return null; } - setCurrentBins({ - ...currentBins, - ...group.reduce((acc: ISelectedBinsProps, bin: string) => ({ - ...acc, - [bin]: { - ...currentBins[bin], - groupName: value, - }, - }), {}), - }); - setWarning(''); - } - } - iconStyle={{ - cursor: 'pointer', - fontSize: '1.8rem', - marginLeft: 10, - }} - isEditing={editingGroupName === groupName} - noEditingStyle={{ fontWeight: 'bold' }} - outsideClickHandlerDisabled={false} - pencilEditingOnly - text={groupName} + } + iconStyle={{ + cursor: 'pointer', + fontSize: '1.8rem', + marginLeft: 10, + }} + isEditing={editingGroupName === groupName} + noEditingStyle={{ fontWeight: 'bold' }} + outsideClickHandlerDisabled={false} + pencilEditingOnly + text={groupName} > - {groupName} - + {groupName} + + {listWarning[groupName] + ? ( + + {listWarning[groupName]} + + ) : null} + ) : (
{`${currentBins[group[0]].key} (${currentBins[group[0]].doc_count})`} @@ -418,7 +444,7 @@ export default compose( listStylePosition: 'inside', paddingLeft: '5px', }} - > + > {`${bin} (${currentBins[bin].doc_count})`} )) @@ -436,26 +462,26 @@ export default compose( justifyContent: 'flex-end', margin: '20px', }} - > + > 0 ? 'visible' : 'hidden', + visibility: globalWarning.length > 0 ? 'visible' : 'hidden', }} - > + > {'Warning: '} - {warning} + {globalWarning} diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index 24c9f0f14..40c12a3e7 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -35,16 +35,18 @@ export default compose( handleSave, isEditing, setIsEditing, - text, value, }) => () => { if (disabled) { return; } if (value.length !== 0) { - setIsEditing(!isEditing); - if (isEditing && value !== text) { - handleSave(value); + if (isEditing) { + if (handleSave(value) !== 'unsave') { + setIsEditing(false); + } + } else { + setIsEditing(true); } } }, @@ -81,7 +83,7 @@ export default compose( toggleEditingAndSave()} - > + > + > setValue(e.target.value)} @@ -108,7 +110,7 @@ export default compose( }} type="text" value={value} - /> + /> {buttonDisplayed ? ( @@ -120,7 +122,7 @@ export default compose( ? `Maximum name length ${MAX_SET_NAME_LENGTH}` : null } - > + > @@ -150,7 +152,7 @@ export default compose( cursor: disabled ? 'not-allowed' : (pencilEditingOnly ? 'default' : 'text'), ...noEditingStyle, }} - > + > {children} + /> )} From a6970894536f566341783eece6f0de48e9cf0eab Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Wed, 5 Jun 2019 13:33:04 -0400 Subject: [PATCH 18/22] Created a new file to handle editable row in GroupValuesModal. --- .../components/Modals/GroupValuesModal.tsx | 112 +++++++------- .../ClinicalVariableCard/index.js | 2 +- .../@ncigdc/uikit/ControlEditableRow.js | 139 ++++++++++++++++++ src/packages/@ncigdc/uikit/EditableLabel.js | 11 +- 4 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 src/packages/@ncigdc/uikit/ControlEditableRow.js diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 378851b95..f697dbdda 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -9,7 +9,7 @@ import { import { Row, Column } from '@ncigdc/uikit/Flex'; import Button from '@ncigdc/uikit/Button'; import { visualizingButton } from '@ncigdc/theme/mixins'; -import EditableLabel from '@ncigdc/uikit/EditableLabel'; +import ControlEditableRow from '@ncigdc/uikit/ControlEditableRow'; const styles = { button: { @@ -143,6 +143,7 @@ export default compose( setSelectedGroupBins, setSelectedHidingBins, setGlobalWarning, + setEditingGroupName, globalWarning, setListWarning, listWarning, @@ -341,6 +342,7 @@ export default compose( { if (Object.keys(selectedHidingBins).length > 0) { setSelectedHidingBins({}); @@ -363,60 +365,66 @@ export default compose( > {group.length > 1 || group[0] !== groupName ? ( - - { - setListWarning({}); - if ( - some(currentBins, (bin: IBinProps) => bin.groupName.trim() === value.trim()) && - groupName.trim() !== value.trim() - ) { - setListWarning({ - ...listWarning, - [groupName]: `"${value.trim()}" already exists.`, - }); - return 'unsave'; - } - setCurrentBins({ - ...currentBins, - ...group.reduce((acc: ISelectedBinsProps, bin: string) => ({ - ...acc, - [bin]: { - ...currentBins[bin], - groupName: value, - }, - }), {}), + { + 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.', }); - setGlobalWarning(''); + } else { setListWarning({}); - setSelectedGroupBins({}); - return null; } - } - iconStyle={{ - cursor: 'pointer', - fontSize: '1.8rem', - marginLeft: 10, - }} - isEditing={editingGroupName === groupName} - noEditingStyle={{ fontWeight: 'bold' }} - outsideClickHandlerDisabled={false} - pencilEditingOnly - text={groupName} - > - {groupName} - - {listWarning[groupName] - ? ( - - {listWarning[groupName]} - - ) : null} - + }} + text={groupName} + warning={listWarning[groupName]} + > + {groupName} + ) : (
{`${currentBins[group[0]].key} (${currentBins[group[0]].doc_count})`} diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index 908adc3c3..6ce6ecc9d 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -1128,7 +1128,7 @@ export default compose( ...acc, [key]: { doc_count: 0, - groupName: bin.groupName, + groupName: bin.groupName !== key ? bin.groupName : '', key, }, }), {}), diff --git a/src/packages/@ncigdc/uikit/ControlEditableRow.js b/src/packages/@ncigdc/uikit/ControlEditableRow.js new file mode 100644 index 000000000..b84ce0bf6 --- /dev/null +++ b/src/packages/@ncigdc/uikit/ControlEditableRow.js @@ -0,0 +1,139 @@ +// @flow +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: ({ setIsEditing, setValue, text }: any) => () => { + setIsEditing(false); + setValue(text); + }, + 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) { + return; + } + if (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/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index 40c12a3e7..09bcb7670 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -76,13 +76,19 @@ export default compose( noEditingStyle, buttonDisplayed = true, outsideClickHandlerDisabled = true, + disableOnKeyDown = false, }) => { return ( {isEditing ? ( toggleEditingAndSave()} + onOutsideClick={() => { + if (disableOnKeyDown) { + return; + } + toggleEditingAndSave(); + }} > setValue(e.target.value)} onFocus={e => e.target.select()} onKeyDown={e => { + if (disableOnKeyDown) { + return; + } if (e.key === 'Enter') { toggleEditingAndSave(); } else if (e.key === 'Escape') { From 399f808451d2221334f84ede0f6d848b9fca99b9 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Wed, 5 Jun 2019 14:02:10 -0400 Subject: [PATCH 19/22] Removed values that equal to 0 when switch cohort. --- .../components/Modals/GroupValuesModal.tsx | 13 ++++++------ .../ClinicalVariableCard/index.js | 21 ++++++++++++------- .../@ncigdc/uikit/ControlEditableRow.js | 13 +++++++----- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index f697dbdda..1acdf71c9 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -135,18 +135,17 @@ export default compose( currentBins, editingGroupName, fieldName, + globalWarning, + listWarning, onClose, onUpdate, selectedGroupBins, selectedHidingBins, setCurrentBins, - setSelectedGroupBins, - setSelectedHidingBins, setGlobalWarning, - setEditingGroupName, - globalWarning, setListWarning, - listWarning, + setSelectedGroupBins, + setSelectedHidingBins, }: IGroupValuesModalProps) => { const groupNameMapping = groupBy( Object.keys(currentBins) @@ -366,6 +365,7 @@ export default compose( {group.length > 1 || group[0] !== groupName ? ( setListWarning({})} containerStyle={{ justifyContent: 'flex-start', }} @@ -422,6 +422,7 @@ export default compose( }} text={groupName} warning={listWarning[groupName]} + > {groupName} @@ -448,8 +449,8 @@ export default compose( style={{ backgroundColor: selectedGroupBins[bin] ? '#d5f4e6' : '', display: 'list-item', - listStyleType: 'disc', listStylePosition: 'inside', + listStyleType: 'disc', paddingLeft: '5px', }} > diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index 6ce6ecc9d..eed1bcf70 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -1124,14 +1124,19 @@ export default compose( fieldName, id, value: { - ...reduce(variable.bins, (acc, bin, key) => ({ - ...acc, - [key]: { - doc_count: 0, - groupName: bin.groupName !== key ? bin.groupName : '', - key, - }, - }), {}), + ...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]: { diff --git a/src/packages/@ncigdc/uikit/ControlEditableRow.js b/src/packages/@ncigdc/uikit/ControlEditableRow.js index b84ce0bf6..a18e6db97 100644 --- a/src/packages/@ncigdc/uikit/ControlEditableRow.js +++ b/src/packages/@ncigdc/uikit/ControlEditableRow.js @@ -15,9 +15,15 @@ export default compose( withState('isEditing', 'setIsEditing', ({ isEditing }: any) => isEditing || false), withState('value', 'setValue', ({ text }: any) => text), withHandlers({ - handleCancel: ({ setIsEditing, setValue, text }: any) => () => { + handleCancel: ({ + cleanWarning, + setIsEditing, + setValue, + text, + }: any) => () => { setIsEditing(false); setValue(text); + cleanWarning(); }, toggleEditingAndSave: ({ disabled = false, @@ -84,10 +90,7 @@ export default compose( }} onFocus={e => e.target.select()} onKeyDown={e => { - if (disableOnKeyDown) { - return; - } - if (e.key === 'Enter') { + if (!disableOnKeyDown && (e.key === 'Enter')) { toggleEditingAndSave(); } else if (e.key === 'Escape') { handleCancel(); From 0ca76c75a2a63c13478060a0b05aa1bfd25bfb36 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Wed, 5 Jun 2019 14:30:54 -0400 Subject: [PATCH 20/22] Restored old editablelabel since I create a new one for groupvaluesmodal. --- .../components/Modals/GroupValuesModal.tsx | 1 - src/packages/@ncigdc/uikit/EditableLabel.js | 172 +++++++----------- 2 files changed, 70 insertions(+), 103 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 1acdf71c9..59ba87a5f 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -422,7 +422,6 @@ export default compose( }} text={groupName} warning={listWarning[groupName]} - > {groupName} diff --git a/src/packages/@ncigdc/uikit/EditableLabel.js b/src/packages/@ncigdc/uikit/EditableLabel.js index 09bcb7670..61e197b70 100644 --- a/src/packages/@ncigdc/uikit/EditableLabel.js +++ b/src/packages/@ncigdc/uikit/EditableLabel.js @@ -14,47 +14,43 @@ import { Row } from '@ncigdc/uikit/Flex'; import Button from '@ncigdc/uikit/Button'; import { visualizingButton } from '@ncigdc/theme/mixins'; import { MAX_SET_NAME_LENGTH } from '@ncigdc/utils/constants'; -import OutsideClickHandler from 'react-outside-click-handler'; import { Tooltip } from './Tooltip/index'; export default compose( - withState('isEditing', 'setIsEditing', ({ isEditing }) => isEditing || false), + withState('isEditing', 'setIsEditing', false), withState('value', 'setValue', ({ text }) => text), defaultProps({ - handleSave: () => { }, + handleSave: value => console.log(value), }), withHandlers({ - handleCancel: ({ setIsEditing, setValue, text }) => () => { + handleCancel: ({ text, setIsEditing, setValue }) => () => { setIsEditing(false); setValue(text); }, }), withHandlers({ toggleEditingAndSave: ({ - disabled = false, - handleSave, isEditing, setIsEditing, value, + text, + handleSave, + handleCancel, + disabled = false, }) => () => { if (disabled) { - return; + return null; } if (value.length !== 0) { - if (isEditing) { - if (handleSave(value) !== 'unsave') { - setIsEditing(false); - } - } else { - setIsEditing(true); + setIsEditing(!isEditing); + if (isEditing && value !== text) { + handleSave(value); } } }, }), lifecycle({ - componentDidUpdate({ - isEditing, setValue, text, value, - }): void { + componentDidUpdate({ text, value, setValue, isEditing }): void { if (!isEditing && value !== text) { setValue(text); } @@ -62,114 +58,87 @@ export default compose( }) )( ({ + text, isEditing, toggleEditingAndSave, value, setValue, + setIsEditing, handleCancel, children, iconStyle = {}, containerStyle = {}, disabled = false, disabledMessage = null, - pencilEditingOnly = false, - noEditingStyle, - buttonDisplayed = true, - outsideClickHandlerDisabled = true, - disableOnKeyDown = false, - }) => { - return ( - + }) => ( +
{isEditing ? ( - { - if (disableOnKeyDown) { - return; - } - toggleEditingAndSave(); + - setValue(e.target.value)} + onKeyDown={e => { + if (e.key === 'Enter') { + toggleEditingAndSave(); + } else if (e.key === 'Escape') { + handleCancel(); + } }} + type="text" + autoFocus + onFocus={e => e.target.select()} + /> + MAX_SET_NAME_LENGTH + ? `Maximum name length ${MAX_SET_NAME_LENGTH}` + : null + } > - setValue(e.target.value)} - onFocus={e => e.target.select()} - onKeyDown={e => { - if (disableOnKeyDown) { - return; - } - if (e.key === 'Enter') { - toggleEditingAndSave(); - } else if (e.key === 'Escape') { - handleCancel(); - } - }} + - - - - ) : null} - - - ) - : ( + > + Save + + + + + ) : ( {children} )} - - ); - } -); +
+ ) +); \ No newline at end of file From 8d7d2bc71005c899bdc85d40d854f74e82582009 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Thu, 6 Jun 2019 10:50:23 -0400 Subject: [PATCH 21/22] Improved types. Added cardFilters variable. --- .../components/Modals/GroupValuesModal.tsx | 37 ++++---- .../ClinicalVariableCard/index.js | 87 ++++++++++--------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 59ba87a5f..0f40d74e8 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -69,8 +69,8 @@ interface IGroupValuesModalProps { children?: ReactNode, globalWarning: string, setGlobalWarning: (globalWarning: string) => void, - setListWarning: any, - listWarning: any, + setListWarning: (listWarning: { [x: string]: string }) => void, + listWarning: { [x: string]: string }, } const blockStyle = { @@ -105,7 +105,7 @@ export default compose( setCurrentBins, setEditingGroupName, setSelectedHidingBins, - }: any) => ({ + }) => ({ binGrouping: () => { const newGroupName = initialName( Object.values(currentBins).map((bin: IBinProps) => bin.groupName), 'selected Value ' @@ -152,6 +152,7 @@ export default compose( .filter((bin: string) => currentBins[bin].groupName !== ''), key => currentBins[key].groupName ); + return (

@@ -171,7 +172,7 @@ export default compose( alignItems: 'flex-end', display: 'flex', }} - > + > Hidden Values

@@ -194,7 +195,7 @@ export default compose( backgroundColor: selectedHidingBins[binKey] ? '#d5f4e6' : '', paddingLeft: '10px', }} - > + > {`${binKey} (${currentBins[binKey].doc_count})`} ))} @@ -224,7 +225,7 @@ export default compose( setListWarning({}); }} style={{ margin: '10px' }} - > + > {'>>'}
@@ -265,7 +266,7 @@ export default compose( alignItems: 'flex-end', display: 'flex', }} - > + > Displayed Values @@ -288,7 +289,7 @@ export default compose( setListWarning({}); }} style={buttonStyle} - > + > {'Reset'} @@ -361,7 +362,7 @@ export default compose( }} - > + > {group.length > 1 || group[0] !== groupName ? ( + > {groupName} ) : ( @@ -452,7 +453,7 @@ export default compose( listStyleType: 'disc', paddingLeft: '5px', }} - > + > {`${bin} (${currentBins[bin].doc_count})`} )) @@ -470,26 +471,26 @@ export default compose( justifyContent: 'flex-end', margin: '20px', }} - > + > 0 ? 'visible' : 'hidden', }} - > + > {'Warning: '} {globalWarning} diff --git a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js index eed1bcf70..e9215641e 100644 --- a/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js +++ b/src/packages/@ncigdc/modern_components/ClinicalAnalysis/ClinicalVariableCard/index.js @@ -146,7 +146,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Box/QQ Plot', }, delete: { @@ -157,7 +157,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Remove Card', }, histogram: { @@ -168,7 +168,7 @@ const vizButtons: IVizButtons = { height: '1em', width: '1em', }} - />), + />), title: 'Histogram', }, survival: { @@ -223,7 +223,7 @@ const getCountLink = ({ doc_count, filters, totalDocs }) => ( filters, searchTableTab: 'cases', }} - > + > {(doc_count || 0).toLocaleString()} {` (${(((doc_count || 0) / totalDocs) * 100).toFixed(2)}%)`} @@ -409,7 +409,7 @@ const getCategoricalTableData = ( }} type="checkbox" value={b.key} - /> + /> ), ...(variable.active_chart === 'survival' ? { @@ -424,7 +424,7 @@ const getCategoricalTableData = ( ? `Click icon to plot ${b.key}` : `Maximum plots (${MAXIMUM_CURVES}) reached` } - > + > @@ -605,6 +605,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : totalDocs; const tsvSubstring = fieldName.replace(/\./g, '-'); + const cardFilters = getCardFilters(variable.plotTypes, selectedBuckets, fieldName, filters); return ( = ({ padding: '0.5rem 1rem 1rem', ...style, }} - > + > + >

+ > {humanify({ term: fieldName })}

@@ -652,7 +653,7 @@ const ClinicalVariableCard: React.ComponentType = ({ : styles.common(theme)), margin: 2, }} - > + > {vizButtons[plotType].title} {vizButtons[plotType].icon} @@ -669,7 +670,7 @@ const ClinicalVariableCard: React.ComponentType = ({ flex: 1, justifyContent: 'center', }} - > + > There is no data for this facet ) @@ -684,7 +685,7 @@ const ClinicalVariableCard: React.ComponentType = ({ fontSize: '1.2rem', marginRight: 10, }} - > + > = ({ style={{ marginRight: 5 }} type="radio" value="percentage" - /> + /> % of Cases = ({ }) } tooltipHTML="Download image or data" - /> + /> {/* {variable.active_chart === 'survival' && (
@@ -837,7 +838,7 @@ const ClinicalVariableCard: React.ComponentType = ({ variable.active_calculation === 'number' ? '#' : '%' } of Cases`, }} - /> + /> )} {variable.active_chart === 'survival' && (
= ({ justifyContent: 'center', margin: '5px 2px 10px', }} - > + > {selectedSurvivalValues.length === 0 ? ( = ({ plotType="clinicalOverall" survivalPlotLoading={survivalPlotLoading} uniqueClass="clinical-survival-plot" - /> + /> ) : ( - + /> )}
)} @@ -889,7 +890,7 @@ const ClinicalVariableCard: React.ComponentType = ({ justifyContent: 'space-between', margin: '5px 0', }} - > + > = ({ ...visualizingButton, padding: '0 12px', }} - > + > Select action )} @@ -906,7 +907,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > downloadToTSV({ filename: `analysis-${ @@ -916,7 +917,7 @@ const ClinicalVariableCard: React.ComponentType = ({ }) } style={styles.actionMenuItem} - > + > Export to TSV = ({ + /> ) ); }} style={styles.actionMenuItem} - > + > Save as new case set = ({ AppendSetButton={AppendExploreCaseSetButton} displayType="case" field="cases.case_id" - filters={getCardFilters(variable.plotTypes, selectedBuckets, fieldName, filters)} + filters={cardFilters} scope="explore" score="gene.gene_id" sort={null} title={`Add ${totalFromSelectedBuckets} Cases to Existing Set`} total={totalFromSelectedBuckets} type="case" - /> + /> ) ); }} style={styles.actionMenuItem} - > + > Add to existing case set = ({ setModal( + /> ) ); }} style={styles.actionMenuItem} - > + > Remove from existing case set @@ -992,7 +993,7 @@ const ClinicalVariableCard: React.ComponentType = ({ ...visualizingButton, padding: '0 12px', }} - > + > Customize Bins )} @@ -1000,7 +1001,7 @@ const ClinicalVariableCard: React.ComponentType = ({ left: 0, minWidth: 205, }} - > + > dispatch( setModal( @@ -1020,12 +1021,12 @@ const ClinicalVariableCard: React.ComponentType = ({ dispatch(setModal(null)); } } - />, + />, ), ) } style={styles.actionMenuItem} - > + > Edit Bins = ({ ); }} style={styles.actionMenuItem} - > + > Reset to Default @@ -1060,7 +1061,7 @@ const ClinicalVariableCard: React.ComponentType = ({ height: 175, }} tableId={`analysis-${tsvSubstring}-table`} - /> + />
)}
@@ -1133,7 +1134,7 @@ export default compose( groupName: bin.groupName, key, }, - } + }; } return acc; }, {}), From 0597b659a5d3aba23e6a9810ab46f8c73ec0bbc0 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Thu, 6 Jun 2019 13:40:24 -0400 Subject: [PATCH 22/22] Fixed comments. --- .../components/Modals/GroupValuesModal.tsx | 19 ++++++++++--------- .../ClinicalVariableCard/index.js | 4 +--- .../@ncigdc/uikit/ControlEditableRow.js | 11 +++++------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx index 0f40d74e8..c22e122b1 100644 --- a/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx +++ b/src/packages/@ncigdc/components/Modals/GroupValuesModal.tsx @@ -472,15 +472,16 @@ export default compose( margin: '20px', }} > - 0 ? 'visible' : 'hidden', - }} - > - {'Warning: '} - {globalWarning} - + {globalWarning.length > 0 ? ( + + {'Warning: '} + {globalWarning} + + ) : null}