Skip to content

Commit

Permalink
enabled filtering for MutationRateSummary
Browse files Browse the repository at this point in the history
Signed-off-by: Onur Sumer <s.onur.sumer@gmail.com>
  • Loading branch information
onursumer committed Sep 19, 2019
1 parent 2bc6c0c commit 902f4d7
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 45 deletions.
81 changes: 38 additions & 43 deletions src/pages/resultsView/mutation/MutationRateSummary.tsx
@@ -1,45 +1,28 @@
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[];
hugoGeneSymbol: string;
samples: SampleIdentifier[];
germlineConsentedSamples: MobxPromise<SampleIdentifier[]>;
molecularProfileIdToMolecularProfile:MobxPromise<{[molecularProfileId:string]:MolecularProfile}>;
mutationStatusFilter?: DataFilter<string>;
onMutationStatusSelect?: (selectedOptionIds: string[], allValuesSelected?: boolean) => void;
}

@observer
export default class MutationRateSummary extends React.Component<IMutationRateSummaryProps, {}>
{
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 (
<div data-test="somaticMutationRate">
<label>Somatic Mutation Frequency:</label>
{rate.toFixed(1)}%

<DefaultTooltip
placement="right"
overlay={(<span>{'Percentage of samples with a somatic mutation in ' + this.props.hugoGeneSymbol}</span>)}
>
<i className="fa fa-info-circle" style={{'marginLeft':5}}></i>
</DefaultTooltip>
</div>
);
}

public germlineMutationFrequency(): JSX.Element
@computed
public get germlineMutationRate()
{
let samples: SampleIdentifier[]|undefined;

Expand All @@ -56,28 +39,40 @@ export default class MutationRateSummary extends React.Component<IMutationRateSu
) ? this.props.germlineConsentedSamples.result : this.props.samples;
}

const gmr = samples ? germlineMutationRate(this.props.hugoGeneSymbol, this.props.mutations, this.props.molecularProfileIdToMolecularProfile.result!, samples) : 0;
return samples ? germlineMutationRate(this.props.hugoGeneSymbol,
this.props.mutations,
this.props.molecularProfileIdToMolecularProfile.result!,
samples): 0;
}

return (
<div data-test='germlineMutationRate' className={(gmr > 0) ? '' : 'invisible' }>
<label>Germline Mutation Frequency:</label>
{(gmr > 0) ? `${gmr.toFixed(1)}%` : '--'}
<DefaultTooltip
placement="right"
overlay={(<span>{'Percentage of samples with a germline mutation in ' + this.props.hugoGeneSymbol}</span>)}
>
<i className="fa fa-info-circle" style={{'marginLeft':5}}></i>
</DefaultTooltip>
</div>
);
@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 (
<div>
{this.somaticMutationFrequency()}
{this.germlineMutationFrequency()}
</div>
<MutationStatusSelector
filter={this.props.mutationStatusFilter}
onSelect={this.props.onMutationStatusSelect}
rates={{
"Germline": this.germlineMutationRate,
"Somatic": this.somaticMutationRate
}}
somaticContent={{
title: "Somatic Mutation Frequency",
description: `Percentage of samples with a somatic mutation in ${this.props.hugoGeneSymbol}`
}}
germlineContent={this.germlineMutationRate > 0 ? {
title: "Germline Mutation Frequency",
description: `Percentage of samples with a germline mutation in ${this.props.hugoGeneSymbol}`
}: undefined}
/>
);
}
}
98 changes: 98 additions & 0 deletions 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 (
<span>
{content.title}
<DefaultTooltip
placement="right"
overlay={
<span>{content.description}</span>
}
>
<i className="fa fa-info-circle" style={{marginLeft: "0.2rem"}} />
</DefaultTooltip>
</span>
);
}
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<MutationStatusSelectorProps, {}>
{
@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 ? (
<BadgeLabel
label={getFilterOptionLabel(this.props.somaticContent)}
badgeContent={`${formatPercentValue(this.props.rates["Somatic"])}%`}
badgeStyleOverride={this.mutationStatusFilterOptions[0].badgeStyleOverride}
/>
): null;
}

private get germlinePlaceholder() {
return <div data-test='germlineMutationRate' className="invisible">%</div>
}

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 ? (
<React.Fragment>
{this.somaticInfo}
{this.germlinePlaceholder}
</React.Fragment>
): (
<MutationStatusBadgeSelector
badgeSelectorOptions={this.mutationStatusFilterOptions}
{...this.props}
/>
);
}
}
26 changes: 24 additions & 2 deletions 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";
Expand All @@ -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
{
Expand All @@ -34,6 +39,10 @@ export default class ResultsViewMutationMapper extends MutationMapper<IResultsVi
super(props);
}

@computed get mutationStatusFilter() {
return this.store.dataStore.dataFilters.find(f => 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 &&
Expand All @@ -47,6 +56,8 @@ export default class ResultsViewMutationMapper extends MutationMapper<IResultsVi
mutations={this.props.store.mutationData.result}
samples={this.props.store.samples.result!}
germlineConsentedSamples={this.props.store.germlineConsentedSamples}
onMutationStatusSelect={this.onMutationStatusSelect}
mutationStatusFilter={this.mutationStatusFilter}
/>
);
} else {
Expand Down Expand Up @@ -112,4 +123,15 @@ export default class ResultsViewMutationMapper extends MutationMapper<IResultsVi
</span>
);
}

@autobind
@action
protected onMutationStatusSelect(selectedMutationStatusIds: string[], allValuesSelected: boolean)
{
onFilterOptionSelect(selectedMutationStatusIds,
allValuesSelected,
this.store.dataStore,
DataFilterType.MUTATION_STATUS,
MUTATION_STATUS_FILTER_ID);
}
}
Expand Up @@ -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[])
{
Expand Down

0 comments on commit 902f4d7

Please sign in to comment.