diff --git a/src/pages/resultsView/mutation/MutationRateSummary.tsx b/src/pages/resultsView/mutation/MutationRateSummary.tsx index c6325a3151f..5694554f9e9 100644 --- a/src/pages/resultsView/mutation/MutationRateSummary.tsx +++ b/src/pages/resultsView/mutation/MutationRateSummary.tsx @@ -1,9 +1,12 @@ import * as React from 'react'; +import {DataFilter} from "react-mutation-mapper"; import {MolecularProfile, Mutation, SampleIdentifier} from "shared/api/generated/CBioPortalAPI"; import {germlineMutationRate, somaticMutationRate} from "shared/lib/MutationUtils"; +import {computed} from "mobx"; import {MobxPromise} from "mobxpromise"; import {observer} from "mobx-react"; -import DefaultTooltip from "public-lib/components/defaultTooltip/DefaultTooltip"; + +import MutationStatusSelector from "./MutationStatusSelector"; export interface IMutationRateSummaryProps { mutations: Mutation[]; @@ -11,35 +14,15 @@ export interface IMutationRateSummaryProps { samples: SampleIdentifier[]; germlineConsentedSamples: MobxPromise; molecularProfileIdToMolecularProfile:MobxPromise<{[molecularProfileId:string]:MolecularProfile}>; + mutationStatusFilter?: DataFilter; + onMutationStatusSelect?: (selectedOptionIds: string[], allValuesSelected?: boolean) => void; } @observer export default class MutationRateSummary extends React.Component { - public somaticMutationFrequency(): JSX.Element - { - let rate = 0; - - if (this.props.samples.length > 0) { - rate = somaticMutationRate(this.props.hugoGeneSymbol, this.props.mutations, this.props.molecularProfileIdToMolecularProfile.result!, this.props.samples); - } - - return ( -
- - {rate.toFixed(1)}% - - {'Percentage of samples with a somatic mutation in ' + this.props.hugoGeneSymbol})} - > - - -
- ); - } - - public germlineMutationFrequency(): JSX.Element + @computed + public get germlineMutationRate() { let samples: SampleIdentifier[]|undefined; @@ -56,28 +39,40 @@ export default class MutationRateSummary extends React.Component 0) ? '' : 'invisible' }> - - {(gmr > 0) ? `${gmr.toFixed(1)}%` : '--'} - {'Percentage of samples with a germline mutation in ' + this.props.hugoGeneSymbol})} - > - - - - ); + @computed + public get somaticMutationRate() + { + return this.props.samples.length > 0 ? somaticMutationRate(this.props.hugoGeneSymbol, + this.props.mutations, + this.props.molecularProfileIdToMolecularProfile.result!, + this.props.samples): 0; } - render() { + render() + { return ( -
- {this.somaticMutationFrequency()} - {this.germlineMutationFrequency()} -
+ 0 ? { + title: "Germline Mutation Frequency", + description: `Percentage of samples with a germline mutation in ${this.props.hugoGeneSymbol}` + }: undefined} + /> ); } } diff --git a/src/pages/resultsView/mutation/MutationStatusSelector.tsx b/src/pages/resultsView/mutation/MutationStatusSelector.tsx new file mode 100644 index 00000000000..24438db3348 --- /dev/null +++ b/src/pages/resultsView/mutation/MutationStatusSelector.tsx @@ -0,0 +1,98 @@ +import {computed} from "mobx"; +import {observer} from "mobx-react"; +import * as React from 'react'; +import { + BadgeLabel, + formatPercentValue, + MutationStatusBadgeSelector, + MutationStatusBadgeSelectorProps +} from "react-mutation-mapper"; + +import DefaultTooltip from "public-lib/components/defaultTooltip/DefaultTooltip"; + + +export function getFilterOptionLabel(content: {title: string, description?: string}): JSX.Element | string +{ + if (content.description) { + return ( + + {content.title} + {content.description} + } + > + + + + ); + } + else { + return content.title; + } +} + +type MutationStatusSelectorProps = MutationStatusBadgeSelectorProps & { + somaticContent: {title: string, description?: string}; + germlineContent?: {title: string, description?: string}; +}; + +@observer +export default class MutationStatusSelector extends React.Component +{ + @computed + private get mutationStatusFilterOptions() + { + const options = [ + { + value: "Somatic", + label: getFilterOptionLabel(this.props.somaticContent), + badgeStyleOverride: {color: "#000", backgroundColor: "#FFF"} + } + ]; + + if (this.props.germlineContent) + { + options.push({ + value: "Germline", + label: getFilterOptionLabel(this.props.germlineContent), + badgeStyleOverride: {color: "#000", backgroundColor: "#FFF"} + }); + } + + return options; + } + + private get somaticInfo() + { + return this.props.rates ? ( + + ): null; + } + + private get germlinePlaceholder() { + return
%
+ } + + public render() + { + // Render the actual selector only if germline content exists. + // Otherwise just display the somatic info without a filter option. + return this.props.germlineContent === undefined ? ( + + {this.somaticInfo} + {this.germlinePlaceholder} + + ): ( + + ); + } +} diff --git a/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx b/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx index b51d3e59cd0..c77d6397321 100644 --- a/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx +++ b/src/pages/resultsView/mutation/ResultsViewMutationMapper.tsx @@ -1,6 +1,8 @@ +import autobind from "autobind-decorator"; import * as React from 'react'; +import {DataFilterType, onFilterOptionSelect} from "react-mutation-mapper"; import {observer} from "mobx-react"; -import {computed} from "mobx"; +import {action, computed} from "mobx"; import {EnsemblTranscript} from "public-lib/api/generated/GenomeNexusAPI"; import DiscreteCNACache from "shared/cache/DiscreteCNACache"; @@ -11,11 +13,14 @@ import GenomeNexusMyVariantInfoCache from "shared/cache/GenomeNexusMyVariantInfo import { IMutationMapperProps, default as MutationMapper } from "shared/components/mutationMapper/MutationMapper"; +import { + MUTATION_STATUS_FILTER_ID +} from "shared/components/mutationMapper/MutationMapperDataStore"; import MutationRateSummary from "pages/resultsView/mutation/MutationRateSummary"; import ResultsViewMutationMapperStore from "pages/resultsView/mutation/ResultsViewMutationMapperStore"; import ResultsViewMutationTable from "pages/resultsView/mutation/ResultsViewMutationTable"; -import {getMobxPromiseGroupStatus} from "../../../shared/lib/getMobxPromiseGroupStatus"; +import {getMobxPromiseGroupStatus} from "shared/lib/getMobxPromiseGroupStatus"; export interface IResultsViewMutationMapperProps extends IMutationMapperProps { @@ -34,6 +39,10 @@ export default class ResultsViewMutationMapper extends MutationMapper f.id === MUTATION_STATUS_FILTER_ID); + } + @computed get mutationRateSummary():JSX.Element|null { // TODO we should not be even calculating mskImpactGermlineConsentedPatientIds for studies other than msk impact if (this.props.store.germlineConsentedSamples && @@ -47,6 +56,8 @@ export default class ResultsViewMutationMapper extends MutationMapper ); } else { @@ -112,4 +123,15 @@ export default class ResultsViewMutationMapper extends MutationMapper ); } + + @autobind + @action + protected onMutationStatusSelect(selectedMutationStatusIds: string[], allValuesSelected: boolean) + { + onFilterOptionSelect(selectedMutationStatusIds, + allValuesSelected, + this.store.dataStore, + DataFilterType.MUTATION_STATUS, + MUTATION_STATUS_FILTER_ID); + } } diff --git a/src/shared/components/mutationMapper/MutationMapperDataStore.ts b/src/shared/components/mutationMapper/MutationMapperDataStore.ts index f51ed694512..7201e0e14d9 100644 --- a/src/shared/components/mutationMapper/MutationMapperDataStore.ts +++ b/src/shared/components/mutationMapper/MutationMapperDataStore.ts @@ -19,6 +19,7 @@ import {countDuplicateMutations, groupMutationsByGeneAndPatientAndProteinChange} type GroupedData = {group: string, data: Mutation[][]}[]; export const PROTEIN_IMPACT_TYPE_FILTER_ID = "_cBioPortalProteinImpactTypeFilter_"; +export const MUTATION_STATUS_FILTER_ID = "_cBioPortalMutationStatusFilter_"; export function findMutationTypeFilter(dataFilters: DataFilter[]) {