import * as React from 'react';
import { Circle } from 'better-react-spinkit';
import classNames from 'classnames';
import {
DefaultTooltip,
TableCellStatusIndicator,
TableCellStatus,
} from 'cbioportal-frontend-commons';
import {
MutationAssessor as MutationAssessorData,
VariantAnnotation,
} from 'genome-nexus-ts-api-client';
import 'rc-tooltip/assets/bootstrap_white.css';
import { Mutation, DiscreteCopyNumberData } from 'cbioportal-ts-api-client';
import MutationAssessor from 'shared/components/annotation/genomeNexus/MutationAssessor';
import Sift from 'shared/components/annotation/genomeNexus/Sift';
import PolyPhen2 from 'shared/components/annotation/genomeNexus/PolyPhen2';
import siftStyles from 'shared/components/annotation/genomeNexus/styles/siftTooltip.module.scss';
import polyPhen2Styles from 'shared/components/annotation/genomeNexus/styles/polyPhen2Tooltip.module.scss';
import mutationAssessorStyles from 'shared/components/annotation/genomeNexus/styles/mutationAssessorColumn.module.scss';
import annotationStyles from 'shared/components/annotation/styles/annotation.module.scss';
import GenomeNexusMutationAssessorCache from 'shared/cache/GenomeNexusMutationAssessorCache';
import GenomeNexusCache, {
GenomeNexusCacheDataType,
} from 'shared/cache/GenomeNexusCache';
import _ from 'lodash';
import { shouldShowMutationAssessor } from 'shared/lib/genomeNexusAnnotationSourcesUtils';
type FunctionalImpactColumnTooltipProps = {
active: 'mutationAssessor' | 'sift' | 'polyPhen2';
};
interface IFunctionalImpactColumnTooltipState {
active: 'mutationAssessor' | 'sift' | 'polyPhen2';
}
enum FunctionalImpactColumnsName {
MUTATION_ASSESSOR,
SIFT,
POLYPHEN2,
}
interface FunctionalImpactData {
mutationAssessor: MutationAssessorData | undefined;
siftScore: number | undefined;
siftPrediction: string | undefined;
polyPhenScore: number | undefined;
polyPhenPrediction: string | undefined;
}
class FunctionalImpactColumnTooltip extends React.Component<
FunctionalImpactColumnTooltipProps,
IFunctionalImpactColumnTooltipState
> {
constructor(props: FunctionalImpactColumnTooltipProps) {
super(props);
this.state = {
active: this.props.active,
};
}
legend() {
const showMutationAssessor = shouldShowMutationAssessor();
return (
| Legend |
{showMutationAssessor && (
this.setState({
active: 'mutationAssessor',
})
}
>
|
)}
this.setState({ active: 'sift' })
}
>
|
this.setState({ active: 'polyPhen2' })
}
>
|
|
|
{showMutationAssessor && (
high
|
)}
deleterious
|
probably_damaging
|
{showMutationAssessor && (
|
|
medium
|
- |
- |
)}
|
|
{showMutationAssessor && (
low
|
)}
deleterious_low_confidence
|
possibly_damaging
|
|
|
{showMutationAssessor && (
neutral
|
)}
tolerated_low_confidence
|
benign
|
|
|
{showMutationAssessor && - | }
tolerated
|
- |
);
}
public static mutationAssessorText() {
return (
Mutation Assessor
{' '}
predicts the functional impact of amino-acid substitutions in
proteins, such as mutations discovered in cancer or missense
polymorphisms. The functional impact is assessed based on
evolutionary conservation of the affected amino acid in protein
homologs. The method has been validated on a large set (60k) of
disease associated (OMIM) and polymorphic variants.
);
}
public static siftText() {
return (
SIFT
{' '}
predicts whether an amino acid substitution affects protein
function based on sequence homology and the physical properties
of amino acids. SIFT can be applied to naturally occurring
nonsynonymous polymorphisms and laboratory-induced missense
mutations.
);
}
public static polyPhen2Text() {
return (
PolyPhen-2
{' '}
(Polymorphism Phenotyping v2) is a tool which predicts possible
impact of an amino acid substitution on the structure and
function of a human protein using straightforward physical and
comparative considerations.
);
}
public render() {
return (
{this.state.active === 'mutationAssessor' &&
FunctionalImpactColumnTooltip.mutationAssessorText()}
{this.state.active === 'sift' &&
FunctionalImpactColumnTooltip.siftText()}
{this.state.active === 'polyPhen2' &&
FunctionalImpactColumnTooltip.polyPhen2Text()}
{this.legend()}
);
}
}
export function placeArrow(tooltipEl: any) {
const arrowEl = tooltipEl.querySelector('.rc-tooltip-arrow');
arrowEl.style.left = '10px';
}
export default class FunctionalImpactColumnFormatter {
public static headerRender(name: string) {
const arrowContent = ;
const showMutationAssessor = shouldShowMutationAssessor();
return (
{name}
{showMutationAssessor && (
}
placement="topLeft"
trigger={['hover', 'focus']}
arrowContent={arrowContent}
destroyTooltipOnHide={true}
onPopupAlign={placeArrow}
>
)}
}
placement="topLeft"
trigger={['hover', 'focus']}
arrowContent={arrowContent}
destroyTooltipOnHide={true}
onPopupAlign={placeArrow}
>
}
placement="topLeft"
trigger={['hover', 'focus']}
arrowContent={arrowContent}
destroyTooltipOnHide={true}
onPopupAlign={placeArrow}
>
);
}
public static getData(
data: Mutation[],
siftPolyphenCache: GenomeNexusCache,
mutationAssessorCache: GenomeNexusMutationAssessorCache
): FunctionalImpactData {
const siftPolyphenCacheData = FunctionalImpactColumnFormatter.getDataFromCache(
data,
siftPolyphenCache
);
const mutationAssessorCacheData = shouldShowMutationAssessor()
? FunctionalImpactColumnFormatter.getDataFromCache(
data,
mutationAssessorCache
)
: null;
const siftData = siftPolyphenCacheData
? this.getSiftData(siftPolyphenCacheData.data)
: undefined;
const polyphenData = siftPolyphenCacheData
? this.getPolyphenData(siftPolyphenCacheData.data)
: undefined;
const mutationAssessor = mutationAssessorCacheData
? this.getMutationAssessorData(mutationAssessorCacheData.data)
: undefined;
const siftScore = siftData && siftData.siftScore;
const siftPrediction = siftData && siftData.siftPrediction;
const polyPhenScore = polyphenData && polyphenData.polyPhenScore;
const polyPhenPrediction =
polyphenData && polyphenData.polyPhenPrediction;
const functionalImpactData: FunctionalImpactData = {
mutationAssessor,
siftScore,
siftPrediction,
polyPhenScore,
polyPhenPrediction,
};
return functionalImpactData;
}
public static getSiftData(siftDataCache: VariantAnnotation | null) {
let siftScore: number | undefined = undefined;
let siftPrediction: string | undefined = undefined;
if (
siftDataCache &&
!_.isEmpty(siftDataCache.transcript_consequences)
) {
siftScore = siftDataCache.transcript_consequences[0].sift_score;
siftPrediction =
siftDataCache.transcript_consequences[0].sift_prediction;
}
return {
siftScore,
siftPrediction,
};
}
public static getPolyphenData(polyphenDataCache: VariantAnnotation | null) {
let polyPhenScore: number | undefined = undefined;
let polyPhenPrediction: string | undefined = undefined;
if (
polyphenDataCache &&
!_.isEmpty(polyphenDataCache.transcript_consequences)
) {
polyPhenScore =
polyphenDataCache.transcript_consequences[0].polyphen_score;
polyPhenPrediction =
polyphenDataCache.transcript_consequences[0]
.polyphen_prediction;
}
return {
polyPhenScore,
polyPhenPrediction,
};
}
public static getMutationAssessorData(
mutationAssessorDataCache: VariantAnnotation | null
): MutationAssessorData | undefined {
if (!mutationAssessorDataCache) {
return undefined;
} else {
return mutationAssessorDataCache.mutation_assessor
? mutationAssessorDataCache.mutation_assessor.annotation
: undefined;
}
}
public static renderFunction(
data: Mutation[],
siftPolyphenCache: GenomeNexusCache | undefined,
mutationAssessorCache: GenomeNexusMutationAssessorCache | undefined
) {
const showMutationAssessor = shouldShowMutationAssessor();
const siftPolyphenCacheData = FunctionalImpactColumnFormatter.getDataFromCache(
data,
siftPolyphenCache
);
const mutationAssessorCacheData = showMutationAssessor
? FunctionalImpactColumnFormatter.getDataFromCache(
data,
mutationAssessorCache
)
: null;
return (
{showMutationAssessor &&
FunctionalImpactColumnFormatter.makeFunctionalImpactViz(
mutationAssessorCacheData,
FunctionalImpactColumnsName.MUTATION_ASSESSOR
)}
{FunctionalImpactColumnFormatter.makeFunctionalImpactViz(
siftPolyphenCacheData,
FunctionalImpactColumnsName.SIFT
)}
{FunctionalImpactColumnFormatter.makeFunctionalImpactViz(
siftPolyphenCacheData,
FunctionalImpactColumnsName.POLYPHEN2
)}
);
}
public static download(
data: Mutation[],
siftPolyphenCache: GenomeNexusCache,
mutationAssessorCache: GenomeNexusMutationAssessorCache
): string {
if (siftPolyphenCache || mutationAssessorCache) {
const functionalImpactData = FunctionalImpactColumnFormatter.getData(
data,
siftPolyphenCache,
mutationAssessorCache
);
let downloadData = [];
if (functionalImpactData) {
if (shouldShowMutationAssessor()) {
downloadData.push(
`MutationAssessor: ${MutationAssessor.download(
functionalImpactData.mutationAssessor
)}`
);
}
downloadData = downloadData.concat([
`SIFT: ${Sift.download(
functionalImpactData.siftScore,
functionalImpactData.siftPrediction
)}`,
`Polyphen-2: ${PolyPhen2.download(
functionalImpactData.polyPhenScore,
functionalImpactData.polyPhenPrediction
)}`,
]);
return downloadData.join(';');
}
}
return '';
}
private static getDataFromCache(
data: Mutation[],
cache: GenomeNexusCache | GenomeNexusMutationAssessorCache | undefined
): GenomeNexusCacheDataType | null {
if (data.length === 0 || !cache) {
return null;
}
return cache.get(data[0]);
}
private static makeFunctionalImpactViz(
cacheData: GenomeNexusCacheDataType | null,
column: FunctionalImpactColumnsName
) {
let status: TableCellStatus | null = null;
if (cacheData === null) {
status = TableCellStatus.LOADING;
} else if (cacheData.status === 'error') {
status = TableCellStatus.ERROR;
} else if (cacheData.data === null) {
status = TableCellStatus.NA;
} else {
let functionalImpactData;
switch (column) {
case FunctionalImpactColumnsName.MUTATION_ASSESSOR:
functionalImpactData = FunctionalImpactColumnFormatter.getMutationAssessorData(
cacheData.data
);
return (
);
case FunctionalImpactColumnsName.SIFT:
functionalImpactData = FunctionalImpactColumnFormatter.getSiftData(
cacheData.data
);
return (
);
case FunctionalImpactColumnsName.POLYPHEN2:
functionalImpactData = FunctionalImpactColumnFormatter.getPolyphenData(
cacheData.data
);
return (
);
}
}
if (status !== null) {
// show loading circle
if (status === TableCellStatus.LOADING) {
return (
);
} else {
return ;
}
}
}
}