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 (
{showMutationAssessor && ( )} {showMutationAssessor && ( )} {showMutationAssessor && ( )} {showMutationAssessor && ( )} {showMutationAssessor && ( )} {showMutationAssessor && }
Legend this.setState({ active: 'mutationAssessor', }) } > Mutation Assessor this.setState({ active: 'sift' }) } > SIFT this.setState({ active: 'polyPhen2' }) } > PolyPhen-2
high deleterious probably_damaging
medium - -
low deleterious_low_confidence possibly_damaging
neutral tolerated_low_confidence benign
- 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} > Sift )} } placement="topLeft" trigger={['hover', 'focus']} arrowContent={arrowContent} destroyTooltipOnHide={true} onPopupAlign={placeArrow} > SIFT } placement="topLeft" trigger={['hover', 'focus']} arrowContent={arrowContent} destroyTooltipOnHide={true} onPopupAlign={placeArrow} > PolyPhen-2
); } 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 ; } } } }