import { OncoKbCardDataType } from 'cbioportal-utils'; import { IndicatorQueryResp } from 'oncokb-ts-api-client'; import * as React from 'react'; import { calcDiagnosticLevelScore, calcOncogenicScore, calcPrognosticLevelScore, calcResistanceLevelScore, calcSensitivityLevelScore, } from '../util/OncoKbUtils'; import { errorIcon, loaderIcon } from './StatusHelpers'; import { AnnotationIcon, AnnotationIconWithTooltip, } from './icon/AnnotationIcon'; import { CompactAnnotationIcon } from './icon/CompactAnnotationIcon'; import { OncoKbTooltip } from './OncoKbTooltip'; import OncoKbFeedback from './OncoKbFeedback'; import './oncokb.scss'; import 'oncokb-styles/dist/oncokb.css'; export interface IOncoKbProps { status: 'pending' | 'error' | 'complete'; indicator?: IndicatorQueryResp; availableDataTypes?: OncoKbCardDataType[]; mergeAnnotationIcons?: boolean; usingPublicOncoKbInstance: boolean; isCancerGene: boolean; geneNotExist: boolean; hugoGeneSymbol: string; userEmailAddress?: string; disableFeedback?: boolean; contentPadding?: number; } export function sortValue( indicator?: IndicatorQueryResp | undefined | null ): number[] { const values: number[] = []; if (indicator) { values[0] = calcOncogenicScore(indicator.oncogenic); values[1] = calcSensitivityLevelScore(indicator.highestSensitiveLevel); values[2] = calcResistanceLevelScore(indicator.highestResistanceLevel); values[3] = calcDiagnosticLevelScore( indicator.highestDiagnosticImplicationLevel ); values[4] = calcPrognosticLevelScore( indicator.highestPrognosticImplicationLevel ); } return values; } export function download( indicator?: IndicatorQueryResp | undefined | null ): string { if (!indicator) { return 'NA'; } const oncogenic = indicator.oncogenic ? indicator.oncogenic : 'Unknown'; const sensitivityLevel = indicator.highestSensitiveLevel?.toLowerCase() || 'level NA'; const resistanceLevel = indicator.highestResistanceLevel?.toLowerCase() || 'resistance NA'; return `${oncogenic}, ${sensitivityLevel}, ${resistanceLevel}`; } function findDefaultDataTypeForTooltip( usingPublicOncoKbInstance: boolean, indicator?: IndicatorQueryResp, availableDataTypes?: OncoKbCardDataType[] ) { if (usingPublicOncoKbInstance || !indicator) { return OncoKbCardDataType.BIOLOGICAL; } // priority is in this order: Tx > Dx > Px > Biological if ( indicator.highestSensitiveLevel && availableDataTypes && availableDataTypes.includes(OncoKbCardDataType.TXS) ) { return OncoKbCardDataType.TXS; } else if ( indicator.highestResistanceLevel && availableDataTypes && availableDataTypes.includes(OncoKbCardDataType.TXR) ) { return OncoKbCardDataType.TXR; } else if ( indicator.highestDiagnosticImplicationLevel && availableDataTypes && availableDataTypes.includes(OncoKbCardDataType.DX) ) { return OncoKbCardDataType.DX; } else if ( indicator.highestPrognosticImplicationLevel && availableDataTypes && availableDataTypes.includes(OncoKbCardDataType.PX) ) { return OncoKbCardDataType.PX; } return OncoKbCardDataType.BIOLOGICAL; } function multiAnnotationIcon( props: IOncoKbProps, handleFeedbackOpen: () => void ) { return ( {levelIcons(props, handleFeedbackOpen)} ); } function levelIcons(props: IOncoKbProps, handleFeedbackOpen: () => void) { if (props.usingPublicOncoKbInstance) { return null; } if (props.indicator) { return ( <> {[ OncoKbCardDataType.TXS, OncoKbCardDataType.TXR, OncoKbCardDataType.DX, OncoKbCardDataType.PX, ].map(dataType => ( ))} ); } else { // workaround: use content padding value to draw an empty icon when there is no indicator data. // this is to keep the icon alignment consistent with the rest of the column. // ideally we should implement grouped columns to avoid these kind of workarounds // (see https://github.com/cBioPortal/cbioportal/issues/8723) return ; } } function singleAnnotationIcon( props: IOncoKbProps, handleFeedbackOpen: () => void ) { return ( } /> ); } function tooltipContent( type: OncoKbCardDataType, props: IOncoKbProps, handleFeedbackOpen: () => void ): JSX.Element { return ( ); } export const OncoKB: React.FunctionComponent = ( props: IOncoKbProps ) => { const [showFeedback, setShowFeedback] = React.useState(false); const [ tooltipDataLoadComplete, setTooltipDataLoadComplete, ] = React.useState(false); const handleFeedbackOpen = React.useCallback( () => setShowFeedback(true), [] ); const handleFeedbackClose = React.useCallback( () => setShowFeedback(false), [] ); let oncoKbContent: JSX.Element; if (props.status === 'error') { oncoKbContent = errorIcon('Error fetching OncoKB data'); } else if (props.status === 'pending') { oncoKbContent = loaderIcon('pull-left'); } else { oncoKbContent = props.mergeAnnotationIcons ? singleAnnotationIcon(props, handleFeedbackOpen) : multiAnnotationIcon(props, handleFeedbackOpen); if (!props.disableFeedback && showFeedback) { oncoKbContent = ( {oncoKbContent} ); } } return oncoKbContent; };