import * as React from 'react'; import { observer } from 'mobx-react'; import { Button, ButtonGroup } from 'react-bootstrap'; import CustomDropdown from './CustomDropdown'; import ConfirmNgchmModal from './ConfirmNgchmModal'; import ReactSelect from 'react-select1'; import { MobxPromise } from 'mobxpromise'; import { action, computed, observable, reaction, makeObservable } from 'mobx'; import _ from 'lodash'; import { SortMode } from '../ResultsViewOncoprint'; import { Gene, MolecularProfile, GenericAssayMeta, } from 'cbioportal-ts-api-client'; import LoadingIndicator from 'shared/components/loadingIndicator/LoadingIndicator'; import { DefaultTooltip, EditableSpan } from 'cbioportal-frontend-commons'; import Slider from 'react-rangeslider'; import 'react-rangeslider/lib/index.css'; import './styles.scss'; import classNames from 'classnames'; import { SpecialAttribute } from '../../../cache/ClinicalDataCache'; import { ResultsViewPageStore } from '../../../../pages/resultsView/ResultsViewPageStore'; import { OncoprintAnalysisCaseType, ExtendedClinicalAttribute, } from '../../../../pages/resultsView/ResultsViewPageStoreUtils'; import OQLTextArea, { GeneBoxType } from '../../GeneSelectionBox/OQLTextArea'; import autobind from 'autobind-decorator'; import { SingleGeneQuery } from '../../../lib/oql/oql-parser'; import TracksMenu from 'pages/resultsView/oncoprint/TracksMenu'; import { GenericAssayTrackInfo } from 'pages/studyView/addChartButton/genericAssaySelection/GenericAssaySelection'; import { IDriverAnnotationControlsHandlers, IDriverAnnotationControlsState, } from 'shared/alterationFiltering/AnnotationFilteringSettings'; import DriverAnnotationControls from 'shared/components/driverAnnotations/DriverAnnotationControls'; import { AppContext } from 'cbioportal-frontend-commons'; import { ClinicalTrackConfig, ClinicalTrackConfigMap, } from 'shared/components/oncoprint/Oncoprint'; export interface IOncoprintControlsHandlers extends IDriverAnnotationControlsHandlers { onSelectColumnType?: (type: 'sample' | 'patient') => void; onSelectShowUnalteredColumns: (unalteredColumnsShown: boolean) => void; onSelectShowWhitespaceBetweenColumns: (showWhitespace: boolean) => void; onSelectShowClinicalTrackLegends?: (showLegends: boolean) => void; onSelectOnlyShowClinicalLegendForAlteredCases?: ( showLegends: boolean ) => void; onSelectShowOqlInLabels?: (show: boolean) => void; onSelectShowMinimap: (showMinimap: boolean) => void; onSelectDistinguishMutationType: (distinguish: boolean) => void; onSelectDistinguishGermlineMutations: (distinguish: boolean) => void; onSelectHideVUS: (hide: boolean) => void; onSelectHideGermlineMutations: (hide: boolean) => void; onSelectSortByMutationType: (sort: boolean) => void; onSelectSortByDrivers: (sort: boolean) => void; onClickSortByData?: () => void; onClickSortAlphabetical?: () => void; onClickSortCaseListOrder?: () => void; onClickDownload?: ( type: 'pdf' | 'png' | 'svg' | 'order' | 'tabular' | 'oncoprinter' ) => void; onChangeSelectedClinicalTracks?: ( trackConfigs: ClinicalTrackConfig[] ) => void; onClickAddGenesToHeatmap?: () => void; onSelectGenericAssayProfile?: (molecularProfileId: string) => void; onClickAddGenericAssays?: (info: GenericAssayTrackInfo[]) => void; onSelectHeatmapProfile?: (molecularProfileId: string) => void; onChangeHeatmapGeneInputValue?: (value: string) => void; onClickNGCHM: () => void; onSetHorzZoom: (z: number) => void; onClickZoomIn: () => void; onClickZoomOut: () => void; } export interface IOncoprintControlsState extends IDriverAnnotationControlsState { showUnalteredColumns: boolean; showWhitespaceBetweenColumns: boolean; showClinicalTrackLegends?: boolean; onlyShowClinicalLegendForAlteredCases?: boolean; showOqlInLabels?: boolean; showMinimap: boolean; isClinicalTrackConfigDirty: boolean; isLoggedIn: boolean; isSessionServiceEnabled: boolean; distinguishMutationType: boolean; distinguishGermlineMutations: boolean; sortByMutationType: boolean; sortByDrivers: boolean; sortByCaseListDisabled: boolean; hidePutativePassengers: boolean; hideGermlineMutations: boolean; sortMode?: SortMode; clinicalAttributesPromise?: MobxPromise; clinicalAttributeSampleCountPromise?: MobxPromise<{ [clinicalAttributeId: string]: number; }>; selectedClinicalAttributeSpecInits?: ClinicalTrackConfigMap; heatmapProfilesPromise?: MobxPromise; genericAssayEntitiesGroupedByGenericAssayTypePromise?: MobxPromise<{ [genericAssayType: string]: GenericAssayMeta[]; }>; selectedHeatmapProfileId?: string; selectedGenericAssayProfile?: MolecularProfile; selectedGenericAssayEntityIds?: string[]; heatmapIsDynamicallyQueried?: boolean; heatmapGeneInputValue?: string; hideHeatmapMenu?: boolean; ngchmButtonActive?: boolean; columnMode?: OncoprintAnalysisCaseType; horzZoom: number; } export interface IOncoprintControlsProps { store?: ResultsViewPageStore; handlers: IOncoprintControlsHandlers; state: IOncoprintControlsState; oncoprinterMode?: boolean; molecularProfileIdToMolecularProfile?: { [molecularProfileId: string]: MolecularProfile; }; selectedGenericAssayEntitiesGroupedByGenericAssayTypeFromUrl?: { [genericAssayType: string]: string[]; }; } export interface ISelectOption { value: string; label: string; } const EVENT_KEY = { columnTypeSample: '0', columnTypePatient: '1', showUnalteredColumns: '2', showWhitespaceBetweenColumns: '3', showClinicalTrackLegends: '4', onlyShowClinicalLegendForAlteredCases: '4.1', showOqlInLabels: '4.2', distinguishMutationType: '5', distinguishGermlineMutations: '5.1', sortByMutationType: '6', sortAlphabetical: '7', sortCaseListOrder: '8', sortByData: '9', sortByDrivers: '10', addGenesToHeatmap: '13', distinguishDrivers: '15', annotateOncoKb: '16', annotateHotspots: '17', annotateCBioPortal: '18', annotateCOSMIC: '19', annotateCBioPortalInput: '20', annotateCOSMICInput: '21', hidePutativePassengers: '22', hideGermlineMutations: '22.1', customDriverBinaryAnnotation: '23', customDriverTierAnnotation: '24', downloadPDF: '25', downloadPNG: '26', downloadSVG: '27', downloadOrder: '28', downloadTabular: '29', downloadOncoprinter: '29.1', horzZoomSlider: '30', viewNGCHM: '31', }; @observer export default class OncoprintControls extends React.Component< IOncoprintControlsProps, {} > { @observable horzZoomSliderState: number; @observable heatmapGenesReady = false; @observable showConfirmNgchmModal: boolean = false; constructor(props: IOncoprintControlsProps) { super(props); makeObservable(this); this.horzZoomSliderState = props.state.horzZoom; reaction( () => this.props.state.horzZoom, z => (this.horzZoomSliderState = z) ); // when horz zoom changes, set slider state } @autobind private onZoomInClick() { this.props.handlers.onClickZoomIn(); } @autobind private onZoomOutClick() { this.props.handlers.onClickZoomOut(); } @autobind private onSetHorzZoomTextInput(val: string) { const percentage = parseFloat(val); const zoom = percentage / 100; this.props.handlers.onSetHorzZoom(zoom); } @autobind private onSelect(eventKey: any) { if (eventKey === EVENT_KEY.distinguishMutationType) { this.props.handlers.onSelectDistinguishMutationType && this.props.handlers.onSelectDistinguishMutationType( !this.props.state.distinguishMutationType ); } } @autobind private onHeatmapProfileSelect(option: { label: string; value: string }) { this.props.handlers.onSelectHeatmapProfile && this.props.handlers.onSelectHeatmapProfile(option.value); } @autobind private toggleShowMinimap() { this.props.handlers.onSelectShowMinimap && this.props.handlers.onSelectShowMinimap( !this.props.state.showMinimap ); } @autobind private onInputClick(event: React.MouseEvent) { switch ((event.target as HTMLInputElement).value) { case EVENT_KEY.showUnalteredColumns: this.props.handlers.onSelectShowUnalteredColumns && this.props.handlers.onSelectShowUnalteredColumns( !this.props.state.showUnalteredColumns ); break; case EVENT_KEY.showWhitespaceBetweenColumns: this.props.handlers.onSelectShowWhitespaceBetweenColumns && this.props.handlers.onSelectShowWhitespaceBetweenColumns( !this.props.state.showWhitespaceBetweenColumns ); break; case EVENT_KEY.showClinicalTrackLegends: this.props.handlers.onSelectShowClinicalTrackLegends && this.props.handlers.onSelectShowClinicalTrackLegends( !this.props.state.showClinicalTrackLegends ); break; case EVENT_KEY.onlyShowClinicalLegendForAlteredCases: this.props.handlers .onSelectOnlyShowClinicalLegendForAlteredCases && this.props.handlers.onSelectOnlyShowClinicalLegendForAlteredCases( !this.props.state.onlyShowClinicalLegendForAlteredCases ); break; case EVENT_KEY.showOqlInLabels: this.props.handlers.onSelectShowOqlInLabels && this.props.handlers.onSelectShowOqlInLabels( !this.props.state.showOqlInLabels ); break; case EVENT_KEY.columnTypeSample: this.props.handlers.onSelectColumnType && this.props.handlers.onSelectColumnType('sample'); break; case EVENT_KEY.columnTypePatient: this.props.handlers.onSelectColumnType && this.props.handlers.onSelectColumnType('patient'); break; case EVENT_KEY.sortByData: this.props.handlers.onClickSortByData && this.props.handlers.onClickSortByData(); break; case EVENT_KEY.sortAlphabetical: this.props.handlers.onClickSortAlphabetical && this.props.handlers.onClickSortAlphabetical(); break; case EVENT_KEY.sortCaseListOrder: this.props.handlers.onClickSortCaseListOrder && this.props.handlers.onClickSortCaseListOrder(); break; case EVENT_KEY.sortByMutationType: this.props.handlers.onSelectSortByMutationType && this.props.handlers.onSelectSortByMutationType( !this.props.state.sortByMutationType ); break; case EVENT_KEY.sortByDrivers: this.props.handlers.onSelectSortByDrivers && this.props.handlers.onSelectSortByDrivers( !this.props.state.sortByDrivers ); break; case EVENT_KEY.distinguishDrivers: this.props.handlers.onSelectDistinguishDrivers && this.props.handlers.onSelectDistinguishDrivers( !this.props.state.distinguishDrivers ); break; case EVENT_KEY.distinguishMutationType: this.props.handlers.onSelectDistinguishMutationType && this.props.handlers.onSelectDistinguishMutationType( !this.props.state.distinguishMutationType ); break; case EVENT_KEY.distinguishGermlineMutations: this.props.handlers.onSelectDistinguishGermlineMutations( !this.props.state.distinguishGermlineMutations ); break; case EVENT_KEY.annotateOncoKb: this.props.handlers.onSelectAnnotateOncoKb && this.props.handlers.onSelectAnnotateOncoKb( !this.props.state.annotateDriversOncoKb ); break; case EVENT_KEY.annotateHotspots: this.props.handlers.onSelectAnnotateHotspots && this.props.handlers.onSelectAnnotateHotspots( !this.props.state.annotateDriversHotspots ); break; case EVENT_KEY.hidePutativePassengers: this.props.handlers.onSelectHideVUS && this.props.handlers.onSelectHideVUS( !this.props.state.hidePutativePassengers ); break; case EVENT_KEY.hideGermlineMutations: this.props.handlers.onSelectHideGermlineMutations( !this.props.state.hideGermlineMutations ); break; case EVENT_KEY.customDriverBinaryAnnotation: this.props.handlers.onSelectCustomDriverAnnotationBinary && this.props.handlers.onSelectCustomDriverAnnotationBinary( !this.props.state.annotateCustomDriverBinary ); break; } } @autobind private onHorzZoomSliderChange(z: number) { this.horzZoomSliderState = z; } @autobind private onHorzZoomSliderSet() { this.props.handlers.onSetHorzZoom(this.horzZoomSliderState); this.horzZoomSliderState = this.props.state.horzZoom; // set it back in case it doesnt change } @autobind private onCustomDriverTierCheckboxClick( event: React.MouseEvent ) { const tier = (event.target as HTMLInputElement).value; this.props.handlers.onSelectCustomDriverAnnotationTier && this.props.handlers.onSelectCustomDriverAnnotationTier( tier, !( this.props.state.selectedCustomDriverAnnotationTiers && this.props.state.selectedCustomDriverAnnotationTiers.get( tier ) ) ); } @autobind private onButtonClick(event: React.MouseEvent) { switch ((event.target as HTMLButtonElement).name) { case EVENT_KEY.addGenesToHeatmap: this.props.handlers.onClickAddGenesToHeatmap && this.props.handlers.onClickAddGenesToHeatmap(); break; case EVENT_KEY.downloadSVG: this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('svg'); break; case EVENT_KEY.downloadPNG: this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('png'); break; case EVENT_KEY.downloadPDF: this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('pdf'); break; case EVENT_KEY.downloadOrder: this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('order'); break; case EVENT_KEY.downloadTabular: this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('tabular'); break; case EVENT_KEY.downloadOncoprinter: this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('oncoprinter'); break; case EVENT_KEY.viewNGCHM: if ( this.props.state.ngchmButtonActive && this.props.handlers.onClickNGCHM ) { this.showConfirmNgchmModal = true; } break; } } @action.bound private onChangeHeatmapGeneInput(oql: any, genes: any, queryStr: string) { this.props.handlers.onChangeHeatmapGeneInputValue && this.props.handlers.onChangeHeatmapGeneInputValue(queryStr); const foundGenes = _.keyBy(genes.found as Gene[], gene => gene.hugoGeneSymbol.toUpperCase() ); this.heatmapGenesReady = _.every( oql.query as SingleGeneQuery[], query => query.gene.toUpperCase() in foundGenes ); // all genes valid } @computed get heatmapProfileOptions() { if ( this.props.state.heatmapProfilesPromise && this.props.state.heatmapProfilesPromise.result ) { return _.map( this.props.state.heatmapProfilesPromise.result, profile => ({ label: profile.name, value: profile.molecularProfileId, type: profile.molecularAlterationType, }) ); } else { return []; } } @observable tabId = 'CLINICAL'; @action.bound private updateTabId(newId: string) { this.tabId = newId; } private tracksMenu = observer(() => { if (this.props.store) { return ( ); } else { return null; } }); @computed get heatmapMenu() { const showGenesTextArea = this.props.state.heatmapIsDynamicallyQueried; if ( this.props.oncoprinterMode || this.props.state.hideHeatmapMenu || !this.props.state.heatmapProfilesPromise ) { return null; } let menu = ; if (this.props.state.heatmapProfilesPromise.isComplete) { if (!this.props.state.heatmapProfilesPromise.result!.length) { return null; } else { menu = (
{showGenesTextArea && [ , , ]} {this.props.state.ngchmButtonActive && [
, Open a new tab to visualize this study as Next Generation Clustered Heatmaps from MD Anderson Cancer Center. } > , ]}
); } } else if (this.props.state.heatmapProfilesPromise.isError) { menu = Error loading heatmap profiles.; } return menu; } private SortMenuOncoprinter = observer(() => { return (
); }); private SortMenuOncoprint = observer(() => { return (
{this.props.state.heatmapProfilesPromise && !( this.props.state.heatmapProfilesPromise .isComplete && !this.props.state.heatmapProfilesPromise.result! .length ) && (
)}
); }); @computed get driverAnnotationSection() { if (this.props.oncoprinterMode || !this.props.store) { return ( <>
Annotate
, this.props.handlers )} resultsView={true} />
Filter
); } else { const store = this.props.store; return ( <>
Annotate and Filter
Please see the menu.
); } } private MutationColorMenu = observer(() => { return (
Color by
{this.driverAnnotationSection}
); }); private ViewMenu = observer(() => { if (this.props.oncoprinterMode) { return ; } else { return ; } }); private ViewMenuOncoprinter = observer(() => { return (
); }); private ViewMenuOncoprint = observer(() => { return ( Data type:

); }); private DownloadMenu = observer(() => { return this.context.showDownloadControls === true ? ( {!this.props.oncoprinterMode && ( )} {!this.props.oncoprinterMode && ( )} ) : null; }); private HorzZoomControls = observer(() => { return (
Zoom out of oncoprint.} placement="top" >
Zoom in/out of oncoprint.} placement="top" >
%
Zoom in to oncoprint.} placement="top" >
); }); @computed get showMinimap() { return this.props.state.showMinimap; } private get minimapButton() { return (
Toggle minimap panel.}>
); } private SortMenu = observer(() => { if (this.props.oncoprinterMode) { return ; } else { return ; } }); render() { return (
{this.minimapButton} (this.showConfirmNgchmModal = false)} openNgchmWindow={this.props.handlers.onClickNGCHM} />
); } } OncoprintControls.contextType = AppContext;