From 6fe0f2bd64da517e314b0daf2f61e101290f17a6 Mon Sep 17 00:00:00 2001 From: Andy Dufilie Date: Fri, 16 Dec 2016 11:37:42 -0500 Subject: [PATCH 001/133] squashed commits for Cancer study selector --- my-index.ejs | 2 +- package.json | 6 +- src/pages/home/HomePage.tsx | 51 +- src/pages/home/styles.module.scss | 9 + .../components/ExperimentalControls.tsx | 132 ++++ src/shared/components/StudyList/StudyList.tsx | 150 +++++ .../components/StudyList/StudyListLogic.ts | 183 ++++++ .../components/StudyList/styles.module.scss | 63 ++ src/shared/components/flexbox/FlexBox.tsx | 53 ++ .../components/flexbox/styles.module.scss | 18 + .../labeledCheckbox/LabeledCheckbox.tsx | 55 ++ .../labeledCheckbox/styles.module.scss | 16 + .../components/query/CancerStudySelector.tsx | 214 +++++++ .../components/query/CancerStudyTreeData.ts | 128 ++++ .../query/GeneticProfileSelector.tsx | 290 +++++++++ .../components/query/QueryContainer.tsx | 99 +++ src/shared/components/query/QueryStore.ts | 63 ++ src/shared/components/query/gene_lists.json | 70 ++ .../query/old/CancerStudySelector.tsx | 604 ++++++++++++++++++ .../components/query/old/QueryConnector.ts | 89 +++ .../components/query/old/QueryContainer.tsx | 120 ++++ .../components/query/old/styles.module.scss | 121 ++++ .../components/query/styles.module.scss | 109 ++++ src/shared/components/tree/DescriptorTree.tsx | 203 ++++++ src/shared/components/tree/styles.module.scss | 24 + src/shared/lib/classNames.ts | 2 +- src/shared/lib/firstDefinedValue.ts | 11 + src/shared/lib/getPromiseResult.ts | 14 + src/shared/lib/memoize.ts | 220 +++++++ typings/missing.d.ts | 1 + 30 files changed, 3099 insertions(+), 21 deletions(-) create mode 100644 src/pages/home/styles.module.scss create mode 100644 src/shared/components/ExperimentalControls.tsx create mode 100644 src/shared/components/StudyList/StudyList.tsx create mode 100644 src/shared/components/StudyList/StudyListLogic.ts create mode 100644 src/shared/components/StudyList/styles.module.scss create mode 100644 src/shared/components/flexbox/FlexBox.tsx create mode 100644 src/shared/components/flexbox/styles.module.scss create mode 100644 src/shared/components/labeledCheckbox/LabeledCheckbox.tsx create mode 100644 src/shared/components/labeledCheckbox/styles.module.scss create mode 100644 src/shared/components/query/CancerStudySelector.tsx create mode 100644 src/shared/components/query/CancerStudyTreeData.ts create mode 100644 src/shared/components/query/GeneticProfileSelector.tsx create mode 100644 src/shared/components/query/QueryContainer.tsx create mode 100644 src/shared/components/query/QueryStore.ts create mode 100644 src/shared/components/query/gene_lists.json create mode 100644 src/shared/components/query/old/CancerStudySelector.tsx create mode 100644 src/shared/components/query/old/QueryConnector.ts create mode 100644 src/shared/components/query/old/QueryContainer.tsx create mode 100644 src/shared/components/query/old/styles.module.scss create mode 100644 src/shared/components/query/styles.module.scss create mode 100644 src/shared/components/tree/DescriptorTree.tsx create mode 100644 src/shared/components/tree/styles.module.scss create mode 100644 src/shared/lib/firstDefinedValue.ts create mode 100644 src/shared/lib/getPromiseResult.ts create mode 100644 src/shared/lib/memoize.ts diff --git a/my-index.ejs b/my-index.ejs index edd167541cb..1ff9f4cd9aa 100644 --- a/my-index.ejs +++ b/my-index.ejs @@ -5,7 +5,7 @@ <%= htmlWebpackPlugin.options.title %> diff --git a/src/shared/components/query/QueryStore.ts b/src/shared/components/query/QueryStore.ts index 0c1405e5b4e..5e625c4ede6 100644 --- a/src/shared/components/query/QueryStore.ts +++ b/src/shared/components/query/QueryStore.ts @@ -5,13 +5,14 @@ import {TypeOfCancer as CancerType, GeneticProfile, CancerStudy, SampleList, Gen import CancerStudyTreeData from "./CancerStudyTreeData"; import StudyListLogic from "../StudyList/StudyListLogic"; import {remoteData} from "../../api/remoteData"; -import {labelMobxPromises, cached} from "mobxpromise"; +import {labelMobxPromises, cached, debounceAsync} from "mobxpromise"; import internalClient from "../../api/cbioportalInternalClientInstance"; import oql_parser from "../../lib/oql/oql-parser"; import {SyntaxError} from "../../lib/oql/oql-parser"; import memoize from "memoize-weak-decorator"; -import debounceAsync from "../../lib/debounceAsync"; import AppConfig from 'appConfig'; +import {getSubmitQueryUrl} from "../../api/urls"; +import {gsUploadByGet} from "../../api/genomespace/gsuploadwindow"; export type GeneReplacement = {alias: string, genes: Gene[]}; @@ -347,6 +348,21 @@ export class QueryStore return this.selectedStudies.reduce((sum:number, study:CancerStudy) => sum + study.allSampleCount, 0); } + // DATA TYPE PRIORITY + + @computed get dataTypePriorityCode():0|1|2 + { + let {mutation, cna} = this.dataTypePriority; + if (mutation && cna) + return 0; + if (mutation) + return 1; + if (cna) + return 2; + + return 0; + } + // GENETIC PROFILE @computed get dict_geneticProfileId_geneticProfile():_.Dictionary @@ -413,6 +429,27 @@ export class QueryStore } } + // DOWNLOAD + + private readonly dict_geneticAlterationType_filenameSuffix:{[K in GeneticProfile['geneticAlterationType']]?: string} = { + "MUTATION_EXTENDED": 'mutations', + "COPY_NUMBER_ALTERATION": 'cna', + "MRNA_EXPRESSION": 'mrna', + "METHYLATION": 'methylation', + "PROTEIN_LEVEL": 'rppa', + }; + + @computed get downloadDataFilename() + { + let study = this.singleSelectedStudyId && this.treeData.map_studyId_cancerStudy.get(this.singleSelectedStudyId); + let profile = this.dict_geneticProfileId_geneticProfile[this.selectedProfileIds[0] as string]; + + if (!this.forDownloadTab || !study || !profile) + return 'cbioportal-data.txt'; + + let suffix = this.dict_geneticAlterationType_filenameSuffix[profile.geneticAlterationType] || profile.geneticAlterationType.toLowerCase(); + return `cbioportal-${study.studyId}-${suffix}.txt`; + } //////////////////////////////////////////////////////////////////////////////// // ACTIONS @@ -472,6 +509,24 @@ export class QueryStore this.replaceGene(geneSymbol, ''); this.geneQuery = normalizeQuery([this.geneQuery, ...toAppend].join(' ')); } + + @action submit() + { + // chooseAction() + } + + @action uploadToGenomeSpace() + { + // if (!validDownloadDataForm(this)) + // return; + + gsUploadByGet({ + url: getSubmitQueryUrl(this), + filename: this.downloadDataFilename, + successCallback: savePath => alert('outer Saved to GenomeSpace as ' + savePath), + errorCallback: savePath => alert('outer ERROR saving to GenomeSpace as ' + savePath), + }); + } } const queryStore = (window as any).queryStore = new QueryStore(); diff --git a/src/shared/components/query/download-data-form-validation.js b/src/shared/components/query/download-data-form-validation.js new file mode 100644 index 00000000000..355c976e493 --- /dev/null +++ b/src/shared/components/query/download-data-form-validation.js @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This file is part of cBioPortal. + * + * cBioPortal is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +*/ + + +function validGeneSet(downloadDataParameters) +{ + var geneIds = ""; + + jQuery.each(downloadDataParameters, function(i, parameter) { + if (parameter.name == "gene_list") { + geneIds = parameter.value; + } + }); + + return !(geneIds == ""); +} + +function validCaseSet(downloadDataParameters) +{ + var caseIds = ""; + var caseSetId = ""; + var missingCaseSetId = true; + var USER_DEFINED_CASELIST = "-1"; + + jQuery.each(downloadDataParameters, function(i, parameter) { + if (parameter.name == "case_set_id") { + missingCaseSetId = false; + caseSetId = parameter.value; + } + else if (parameter.name == "case_ids") { + caseIds = parameter.value; + } + }); + + return !(missingCaseSetId || (caseSetId == USER_DEFINED_CASELIST && caseIds == "")); +} + +function validGenomicProfile(downloadDataParameters) +{ + var foundGeneticProfileIds = false; + + jQuery.each(downloadDataParameters, function(i, parameter) { + if (parameter.name == "genetic_profile_ids") { + foundGeneticProfileIds = true; + } + }); + + return foundGeneticProfileIds; +} + +export function validDownloadDataForm(downloadDataParameters) +{ + if (!validGenomicProfile(downloadDataParameters)) { + alert("Invalid Genomic Profile Selected."); + return false; + } + + if (!validCaseSet(downloadDataParameters)) { + alert("Invalid Patient/Case Set."); + return false; + } + + if (!validGeneSet(downloadDataParameters)) { + alert("Invalid Gene Set."); + return false; + } + + return true; +} diff --git a/src/shared/lib/debounceAsync.ts b/src/shared/lib/debounceAsync.ts deleted file mode 100644 index f1e84eb9a79..00000000000 --- a/src/shared/lib/debounceAsync.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * A function created with debounceAsync() returns a new Promise - * every time, but only the last promise created before invoking the - * original function will be resolved after a specified delay. - * - * @author adufilie http://github.com/adufilie - */ - -export default function debounceAsync PromiseLike>(invoke:F, delay = 0):F -{ - function invokeLater( - context:any, - args:any[], - resolve:(result:PromiseLike)=>void, - reject:(error:Error)=>void - ) { - try - { - resolve(invoke.apply(context, args)); - } - catch (e) - { - reject(e); - } - } - - let timeout = 0; - return function(...args:any[]):PromiseLike { - return new Promise( - function(resolve, reject) { - window.clearTimeout(timeout); - timeout = window.setTimeout(invokeLater, delay, this, args, resolve, reject); - } - ); - } as F; -} diff --git a/typings/build-url.d.ts b/typings/build-url.d.ts new file mode 100644 index 00000000000..454d974c4c4 --- /dev/null +++ b/typings/build-url.d.ts @@ -0,0 +1,12 @@ +declare module 'build-url' +{ + type QueryParams = {[key:string]: undefined | string | ReadonlyArray}; + type Params = { + path?: string, + hash?: string, + queryParams?: QueryParams + } + function buildUrl(base:string|null, params:Params):string; + function buildUrl(params:Params):string; + export = buildUrl; +} From 4c45793aed3744c35e3f4171a7d83afc546c11c7 Mon Sep 17 00:00:00 2001 From: Brittany Storoz Date: Fri, 10 Mar 2017 09:50:50 -0500 Subject: [PATCH 078/133] Fix max-widths on labels --- src/shared/components/StudyList/styles.module.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared/components/StudyList/styles.module.scss b/src/shared/components/StudyList/styles.module.scss index 314f76d3766..48bf2c9cb5c 100644 --- a/src/shared/components/StudyList/styles.module.scss +++ b/src/shared/components/StudyList/styles.module.scss @@ -53,6 +53,7 @@ ul.StudyList { + margin: 0; padding: 0 15px; } @@ -159,6 +160,7 @@ ul.StudyList { label { font: normal normal 400 15px/18px 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #1982b8; + margin: 0; padding: 5px 15px 5px 0; } } @@ -167,6 +169,7 @@ ul.StudyList { label { font: normal normal 600 11px/18px 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #444; + margin: 0; padding: 0px 15px 0px 0; text-transform: uppercase; } @@ -194,7 +197,7 @@ li.Study { .StudyName { color: #444; - max-width: 415px; + max-width: 380px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; From 406533717bb2520b52f1040b54229ee2ac79e3ad Mon Sep 17 00:00:00 2001 From: Brittany Storoz Date: Fri, 10 Mar 2017 10:09:17 -0500 Subject: [PATCH 079/133] Fix label alignments --- src/shared/components/query/styles.module.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shared/components/query/styles.module.scss b/src/shared/components/query/styles.module.scss index a73294c0215..ba86719551e 100644 --- a/src/shared/components/query/styles.module.scss +++ b/src/shared/components/query/styles.module.scss @@ -283,12 +283,14 @@ } .DataTypePrioritySelector { + align-items: flex-end; + h2 { flex-grow: 0; } input[type="radio"] { - margin: 4px 6px 0 0; + margin: 0 6px 0 12px; } } From 4cb3e4b4eea94e798a9fca733b7e108b57fba4af Mon Sep 17 00:00:00 2001 From: Brittany Storoz Date: Fri, 10 Mar 2017 12:38:11 -0500 Subject: [PATCH 080/133] Update button styles --- src/globalStyles/global.scss | 38 +++++++++++++++---- .../components/StudyList/styles.module.scss | 1 + .../components/query/CancerStudySelector.tsx | 4 +- .../components/query/QueryContainer.tsx | 3 +- .../components/query/styles.module.scss | 28 ++------------ 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/globalStyles/global.scss b/src/globalStyles/global.scss index 6700490c7ba..a8b3ff7b35e 100755 --- a/src/globalStyles/global.scss +++ b/src/globalStyles/global.scss @@ -11,14 +11,37 @@ body { /* fix for legacy style isses. makes it bootstrap */ #content .cbioportal-frontend { - h4 { - font-size:18px; - margin-top:10px; - margin-bottom:10px; - font-weight:500; - line-height:1.1; - color:$text-color; + h4 { + color: $text-color; + font-size: 18px; + margin-top: 10px; + margin-bottom: 10px; + font-weight: 500; + line-height: 1.1; + } +} + + /* Buttons */ +.cbioportal-frontend { + + .cta { + background: rgb(17, 116, 167); + border: 1px solid rgba(0,0,0,0.05); + border-radius: 2px; + color: rgba(255, 255, 255, 0.90); + display: block; + text-align: center; + font: normal normal 500 11px/19px 'Helvetica Neue', Helvetica, Arial, sans-serif; + padding: 5px 10px; + box-shadow: inset 1px 1px 0px rgba(255, 255, 255, 0.1); + + &:hover { + cursor: pointer; + background: #1f688e; + color: #fff; + } } + } .topBanner { @@ -26,6 +49,7 @@ body { margin:0 auto; } +/* Tables */ th.reactable-header-sortable { &:focus {outline:0;} diff --git a/src/shared/components/StudyList/styles.module.scss b/src/shared/components/StudyList/styles.module.scss index 48bf2c9cb5c..786970e31ad 100644 --- a/src/shared/components/StudyList/styles.module.scss +++ b/src/shared/components/StudyList/styles.module.scss @@ -172,6 +172,7 @@ ul.StudyList { margin: 0; padding: 0px 15px 0px 0; text-transform: uppercase; + white-space: nowrap; } } } diff --git a/src/shared/components/query/CancerStudySelector.tsx b/src/shared/components/query/CancerStudySelector.tsx index e3ec7c70c04..0fbd1134b9c 100644 --- a/src/shared/components/query/CancerStudySelector.tsx +++ b/src/shared/components/query/CancerStudySelector.tsx @@ -19,8 +19,8 @@ const styles = styles_any as { cancerStudySelectorHeader: string, selectable: string, selected: string, - selectAll: string, selectedCount: string, + selectAll: string, noData: string, selectionsExist: string, cancerStudyName: string, @@ -199,7 +199,7 @@ export default class CancerStudySelector extends React.Component - logic.hack_handleSelectAll(!allSelected)}> + logic.hack_handleSelectAll(!allSelected)}> {allSelected ? "Deselect All" : "Select All"} diff --git a/src/shared/components/query/QueryContainer.tsx b/src/shared/components/query/QueryContainer.tsx index 52d854560a2..b0b759e0252 100644 --- a/src/shared/components/query/QueryContainer.tsx +++ b/src/shared/components/query/QueryContainer.tsx @@ -5,6 +5,7 @@ import Dictionary = _.Dictionary; import CancerStudySelector from "./CancerStudySelector"; import {FlexRow, FlexCol} from "../flexbox/FlexBox"; import * as styles_any from './styles.module.scss'; +import classNames from "../../lib/classNames"; import GeneticProfileSelector from "./GeneticProfileSelector"; import {observer} from "mobx-react"; import queryStore from "./QueryStore"; @@ -119,7 +120,7 @@ export default class QueryContainer extends React.Component<{}, {}> )} - {!!(this.store.forDownloadTab) && ( diff --git a/src/shared/components/query/styles.module.scss b/src/shared/components/query/styles.module.scss index ba86719551e..2a8d58a3e0f 100644 --- a/src/shared/components/query/styles.module.scss +++ b/src/shared/components/query/styles.module.scss @@ -99,23 +99,7 @@ } .selectAll { - background: rgb(17, 116, 167); - border: 1px solid rgba(0,0,0,0.05); - border-radius: 2px; - display: block; - text-align: center; text-transform: uppercase; - color: rgba(255, 255, 255, 0.90); - font: normal normal 500 11px/19px 'Helvetica Neue', Helvetica, Arial, sans-serif; - padding: 5px 10px; - box-shadow: inset 1px 1px 0px rgba(255, 255, 255, 0.1); - min-width: 115px; - - &:hover { - cursor: pointer; - background: #1f688e; - color: #fff; - } } .noData, @@ -512,16 +496,10 @@ label.transposeDataMatrix { div.submitRow { button.submit { - font-weight: 400; + font-weight: 300; font-size: 18px; - width: 235px; - padding: 10px 16px; - background-color: #e6e6e6; - border: 1px solid #ccc; - border-radius: 6px; - &:hover { - border-color: #adadad; - } + width: 100%; + padding: 10px 0; } button.genomeSpace { From 00f3d0ee28a656e5ef3ccaec9802cbfc9e73c1ea Mon Sep 17 00:00:00 2001 From: Andy Dufilie Date: Fri, 10 Mar 2017 14:53:12 -0500 Subject: [PATCH 081/133] oql syntax error highlight works now --- src/shared/api/urls.ts | 33 ++-- .../components/query/GeneSetSelector.tsx | 26 +++- .../components/query/GeneSymbolValidator.tsx | 26 ++-- .../components/query/QueryContainer.tsx | 1 + src/shared/components/query/QueryStore.ts | 141 ++++++++++++++++-- .../components/query/SampleListSelector.tsx | 3 +- src/shared/lib/oql/oql-parser.d.ts | 125 +++++++++++++++- typings/build-url.d.ts | 4 +- 8 files changed, 306 insertions(+), 53 deletions(-) diff --git a/src/shared/api/urls.ts b/src/shared/api/urls.ts index 4061804a8e1..e352b1570cb 100644 --- a/src/shared/api/urls.ts +++ b/src/shared/api/urls.ts @@ -28,22 +28,23 @@ export function getStudyViewUrl(studyId:string) { export function getStudySummaryUrl(studyId:string) { return cbioUrl('study', {id: studyId}, 'summary'); } -export function getSubmitQueryUrl(store:QueryStore) -{ - return cbioUrl('index.do', { - cancer_study_list: store.selectedStudyIds, - cancer_study_id: store.singleSelectedStudyId, - genetic_profile_ids_PROFILE_MUTATION_EXTENDED: '', - data_priority: store.dataTypePriorityCode + '', - case_set_id: store.selectedSampleListId, - case_ids: store.caseIds, - patient_case_select: store.caseIdsMode, - gene_set_choice: 'user-defined-list', - gene_list: store.geneQuery, - clinical_param_selection: '', - tab_index: store.forDownloadTab ? 'tab_download' : 'tab_visualize', - Action: 'Submit', - }); + +type SubmitQueryUrlParams = { + cancer_study_list: ReadonlyArray, + cancer_study_id: string, + genetic_profile_ids_PROFILE_MUTATION_EXTENDED: '', + data_priority: '0'|'1'|'2', + case_set_id: string, + case_ids: string, + patient_case_select: 'sample' | 'patient', + gene_set_choice: 'user-defined-list', + gene_list: string, + clinical_param_selection: '', + tab_index: 'tab_download' | 'tab_visualize', + Action: 'Submit', +}; +export function getSubmitQueryUrl(params:SubmitQueryUrlParams) { + return cbioUrl('index.do', params); } export function getPubMedUrl(pmid:string) { return `http://www.ncbi.nlm.nih.gov/pubmed/${pmid}`; diff --git a/src/shared/components/query/GeneSetSelector.tsx b/src/shared/components/query/GeneSetSelector.tsx index cc58ceedcb8..0562954a050 100644 --- a/src/shared/components/query/GeneSetSelector.tsx +++ b/src/shared/components/query/GeneSetSelector.tsx @@ -10,6 +10,7 @@ import GeneSymbolValidator from "./GeneSymbolValidator"; import classNames from "../../lib/classNames"; import AsyncStatus from "../asyncStatus/AsyncStatus"; import {getOncoQueryDocUrl} from "../../api/urls"; +import {QueryStore} from "./QueryStore"; const styles = styles_any as { GeneSetSelector: string, @@ -20,8 +21,12 @@ const styles = styles_any as { notEmpty: string, }; +export interface GeneSetSelectorProps +{ +} + @observer -export default class GeneSetSelector extends React.Component<{}, {}> +export default class GeneSetSelector extends React.Component { get store() { @@ -48,6 +53,20 @@ export default class GeneSetSelector extends React.Component<{}, {}> ]; } + @computed get textAreaRef() + { + if (this.store.geneQueryErrorDisplayStatus === 'shouldFocus') + return (textArea:HTMLTextAreaElement) => { + let {error} = this.store.oql; + if (textArea && error) + { + textArea.focus(); + textArea.setSelectionRange(error.start, error.end); + this.store.geneQueryErrorDisplayStatus = 'focused'; + } + }; + } + render() { return ( @@ -79,15 +98,14 @@ export default class GeneSetSelector extends React.Component<{}, {}>