diff --git a/src/main/webapp/app/components/LevelWithDescription.tsx b/src/main/webapp/app/components/LevelWithDescription.tsx index cb73ddae8..8f5de178c 100644 --- a/src/main/webapp/app/components/LevelWithDescription.tsx +++ b/src/main/webapp/app/components/LevelWithDescription.tsx @@ -8,7 +8,7 @@ import ReactHtmlParser from 'react-html-parser'; import { LEVELS } from 'app/config/constants'; export const LevelWithDescription: React.FunctionComponent<{ - level: LEVELS; + level: LEVELS | undefined; appStore?: AppStore; description?: string; }> = inject('appStore')(props => { diff --git a/src/main/webapp/app/components/PageContainer.tsx b/src/main/webapp/app/components/PageContainer.tsx index 6b52726b4..319c9ad5a 100644 --- a/src/main/webapp/app/components/PageContainer.tsx +++ b/src/main/webapp/app/components/PageContainer.tsx @@ -4,7 +4,10 @@ import WindowStore from 'app/store/WindowStore'; import { RouterStore } from 'mobx-react-router'; import { PAGE_ROUTE } from 'app/config/constants'; import { GENETIC_TYPE } from 'app/components/geneticTypeTabs/GeneticTypeTabs'; -import { parseGenePagePath } from 'app/shared/utils/UrlUtils'; +import { + parseGenePagePath, + parseAlterationPagePath, +} from 'app/shared/utils/UrlUtils'; const Container: FunctionComponent<{ inGenePage: boolean; @@ -25,18 +28,23 @@ const PageContainer: React.FunctionComponent<{ }> = props => { const genePagePath = parseGenePagePath(props.routing.location.pathname); const inGenePage = genePagePath.geneticType !== undefined; + const inAlterationPage = + parseAlterationPagePath(props.routing.location.pathname).geneticType !== + undefined; return (
- {props.children} + + {props.children} +
); diff --git a/src/main/webapp/app/config/constants.tsx b/src/main/webapp/app/config/constants.tsx index 0db2981ab..f686a8fc0 100644 --- a/src/main/webapp/app/config/constants.tsx +++ b/src/main/webapp/app/config/constants.tsx @@ -604,6 +604,8 @@ export enum PAGE_ROUTE { SOMATIC_GENE = '/gene/:hugoSymbol/somatic', GERMLINE_GENE = '/gene/:hugoSymbol/germline', ALTERATION = '/gene/:hugoSymbol/:alteration', + SOMATIC_ALTERATION = '/gene/:hugoSymbol/somatic/:alteration', + GERMLINE_ALTERATION = '/gene/:hugoSymbol/germline/:alteration', HGVSG = '/hgvsg', HGVSG_WITH_QUERY = '/hgvsg/:query', GENOMIC_CHANGE = '/genomic-change', diff --git a/src/main/webapp/app/pages/genePage/GeneInfo.tsx b/src/main/webapp/app/pages/genePage/GeneInfo.tsx index 5792aa3c7..34b463662 100644 --- a/src/main/webapp/app/pages/genePage/GeneInfo.tsx +++ b/src/main/webapp/app/pages/genePage/GeneInfo.tsx @@ -16,7 +16,7 @@ enum GENE_TYPE_DESC { TUMOR_SUPPRESSOR = 'Tumor Suppressor', } -const HighestLevelItem: React.FunctionComponent<{ +export const HighestLevelItem: React.FunctionComponent<{ level: LEVELS; key?: string; }> = props => { diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.module.scss b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.module.scss new file mode 100644 index 000000000..94d9ce84d --- /dev/null +++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.module.scss @@ -0,0 +1,27 @@ +.itemLink a { + justify-content: initial; +} + +.itemHeader { + font-size: 1rem; + color: #878d96; +} + +.alterationTile { + padding: 16px; + padding-bottom: 24px; + min-width: 347px; + max-width: 392px; + gap: 8px; + box-shadow: $default-box-shadow; + border-radius: 8px; +} + +.alterationTileGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); +} + +.alterationTileItems { + gap: 16px; +} diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.tsx b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.tsx new file mode 100644 index 000000000..117558eb4 --- /dev/null +++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import styles from './AlterationTile.module.scss'; +import classNames from 'classnames'; +import ExternalLinkIcon from 'app/shared/icons/ExternalLinkIcon'; + +type AlterationItemProps = + | { + title: string; + value: JSX.Element | string; + link?: undefined; + } + | { + title: string; + value: string; + link: string; + }; + +function AlterationItem({ + title: itemTitle, + value, + link, +}: AlterationItemProps) { + return ( +
+

{itemTitle}

+
+ {typeof value === 'string' ? ( +
+ {link ? ( +
+ {value} +
+ ) : ( + value + )} +
+ ) : ( + value + )} +
+
+ ); +} + +type AlterationTileProps = { + title: string; + items: (AlterationItemProps | [AlterationItemProps, AlterationItemProps])[]; +}; + +export default function AlterationTile({ + title, + items, +}: AlterationTileProps): JSX.Element { + return ( +
+

{title}

+
+ {items.map((parent, i) => { + if (Array.isArray(parent)) { + return ( +
+ {parent.map((child, j) => { + return ; + })} +
+ ); + } else { + return ; + } + })} +
+
+ ); +} diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/HighestLevelEvidence.tsx b/src/main/webapp/app/pages/somaticGermlineAlterationPage/HighestLevelEvidence.tsx new file mode 100644 index 000000000..6f7c0b609 --- /dev/null +++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/HighestLevelEvidence.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { + levelOfEvidence2Level, + OncoKBLevelIcon, + FdaLevelIcon, +} from 'app/shared/utils/Utils'; + +type HighestLevelEvidenceProp = { + type: + | 'Sensitive' + | 'Resistance' + | 'DiagnosticImplication' + | 'PrognosticImplication' + | 'Fda'; + level: string | undefined; +}; + +export default function HighestLevelEvidence({ + type, + level: rawLevel = '', +}: HighestLevelEvidenceProp): JSX.Element { + if (type === 'Sensitive') { + const level = levelOfEvidence2Level(rawLevel, false); + return ( + + ); + } + if (type === 'Resistance') { + const level = levelOfEvidence2Level(rawLevel, false); + return ( + + ); + } + if (type === 'DiagnosticImplication') { + const level = levelOfEvidence2Level(rawLevel, false); + return ( + + ); + } + if (type === 'PrognosticImplication') { + const level = levelOfEvidence2Level(rawLevel, false); + return ( + + ); + } + if (type === 'Fda') { + const level = levelOfEvidence2Level(rawLevel, false); + return ; + } + return <>; +} diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.module.scss b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.module.scss new file mode 100644 index 000000000..763372ffc --- /dev/null +++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.module.scss @@ -0,0 +1,37 @@ +.backLink { + gap: 0.5rem; +} + +.header { + margin-bottom: 0.5rem; +} + +.headerContent { + gap: 0.5rem; +} + +.navBarTitle { + .pill { + font-size: 0.85rem; + } +} + +.pill { + font-size: 1rem; + border: #0968c3; + color: #0968c3; + border-style: solid; + border-width: 2px; + border-radius: 56px; + padding: 0px 8px; + background: #f0f5ff; +} + +.alterationTiles { + gap: 8px; + padding: 32px 0px; +} + +.descriptionContainer { + margin-bottom: 1rem; +} diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.tsx b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.tsx new file mode 100644 index 000000000..9d84269f5 --- /dev/null +++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.tsx @@ -0,0 +1,774 @@ +import React from 'react'; +import { Helmet } from 'react-helmet-async'; +import { + AnnotationStore, + TherapeuticImplication, + FdaImplication, +} from 'app/store/AnnotationStore'; +import { AnnotationType } from '../annotationPage/AnnotationPage'; +import { RouteComponentProps } from 'react-router'; +import * as QueryString from 'query-string'; +import { + decodeSlash, + getPageTitle, + levelOfEvidence2Level, + getCancerTypeNameFromOncoTreeType, + getCancerTypesName, + getTreatmentNameByPriority, + articles2Citations, + isCategoricalAlteration, +} from 'app/shared/utils/Utils'; +import { + getAlterationPageLink, + parseAlterationPagePath, + getGenePageLink, + AlterationPageLink, +} from 'app/shared/utils/UrlUtils'; +import { computed, reaction, action, observable } from 'mobx'; +import { GENETIC_TYPE } from 'app/components/geneticTypeTabs/GeneticTypeTabs'; +import { observer, inject } from 'mobx-react'; +import { Link } from 'react-router-dom'; +import styles from './SomaticGermlineAlterationPage.module.scss'; +import classNames from 'classnames'; +import StickyMiniNavBar from 'app/shared/nav/StickyMiniNavBar'; +import AlterationView from '../annotationPage/AlterationView'; +import AppStore from 'app/store/AppStore'; +import ShowHideText from 'app/shared/texts/ShowHideText'; +import { Col, Alert } from 'reactstrap'; +import { Row, Container } from 'react-bootstrap'; +import { CancerTypeView } from '../annotationPage/CancerTypeView'; +import WindowStore from 'app/store/WindowStore'; +import AuthenticationStore from 'app/store/AuthenticationStore'; +import { + EVIDENCE_TYPES, + REFERENCE_GENOME, + ANNOTATION_PAGE_TAB_KEYS, + TREATMENT_EVIDENCE_TYPES, + ONCOKB_TM, +} from 'app/config/constants'; +import { Alteration } from 'app/shared/api/generated/OncoKbAPI'; +import { + Evidence, + VariantAnnotationTumorType, +} from 'app/shared/api/generated/OncoKbPrivateAPI'; +import WithSeparator from 'react-with-separator'; +import { uniqBy, upperFirst } from 'app/shared/utils/LodashUtils'; +import { + SummaryKey, + getSummaries, + getUniqueFdaImplications, +} from '../annotationPage/Utils'; +import autobind from 'autobind-decorator'; +import { AlterationPageHashQueries } from 'app/shared/route/types'; +import MutationEffectDescription from '../annotationPage/MutationEffectDescription'; +import MiniNavBarHeader from 'app/shared/nav/MiniNavBarHeader'; +import AlterationTile from './AlterationTile'; +import HighestLevelEvidence from './HighestLevelEvidence'; +import { GenomicIndicatorTable } from '../genePage/GenomicIndicatorTable'; +import { Else, If, Then } from 'react-if'; +import LoadingIndicator, { + LoaderSize, +} from 'app/components/loadingIndicator/LoadingIndicator'; + +type MatchParams = { + hugoSymbol: string; + alteration: string; +}; + +type SomaticGermlineAlterationPageProps = { + appStore: AppStore; + windowStore: WindowStore; + authenticationStore: AuthenticationStore; +} & RouteComponentProps; + +type SomaticGermlineAlterationPageState = {}; + +@inject('appStore', 'windowStore', 'authenticationStore') +@observer +export class SomaticGermlineAlterationPage extends React.Component< + SomaticGermlineAlterationPageProps, + SomaticGermlineAlterationPageState +> { + private store: AnnotationStore; + private selectedTab: ANNOTATION_PAGE_TAB_KEYS; + + @observable showMutationEffect = true; + + constructor(props: SomaticGermlineAlterationPageProps) { + super(props); + const alterationQuery = decodeSlash(props.match.params.alteration); + reaction( + () => [this.geneticType], + ([geneticType]) => { + if (props.match.params) { + this.store = new AnnotationStore({ + type: alterationQuery + ? AnnotationType.PROTEIN_CHANGE + : AnnotationType.GENE, + hugoSymbolQuery: props.match.params.hugoSymbol, + alterationQuery, + germline: geneticType === GENETIC_TYPE.GERMLINE, + }); + if (this.store.cancerTypeName) { + this.showMutationEffect = false; + } + } + }, + true + ); + reaction( + () => [props.location.hash], + ([hash]) => { + const queryStrings = QueryString.parse( + hash + ) as AlterationPageHashQueries; + if (queryStrings.tab) { + this.selectedTab = queryStrings.tab; + if (queryStrings.tab === ANNOTATION_PAGE_TAB_KEYS.FDA) { + this.props.appStore.inFdaRecognizedContent = true; + } + } + }, + true + ); + } + + @action.bound + toggleMutationEffect(value: boolean) { + this.showMutationEffect = value; + } + + @computed + get pageShouldBeRendered() { + return ( + this.store.gene.isComplete && + this.store.geneNumber.isComplete && + this.store.ensemblGenes.isComplete && + this.store.clinicalAlterations.isComplete && + this.store.biologicalAlterations.isComplete && + this.store.annotationData.isComplete + ); + } + + @computed + get errorOccurred() { + return ( + this.store.gene.isError || + this.store.geneNumber.isError || + this.store.ensemblGenes.isError || + this.store.clinicalAlterations.isError || + this.store.biologicalAlterations.isError || + this.store.annotationData.isError + ); + } + + @computed + get documentTitle() { + const content = []; + if (this.store.hugoSymbol) { + content.push(this.store.hugoSymbol); + } + if (this.store.alterationQuery) { + content.push(this.store.alterationQuery); + } + if (this.store.tumorTypeQuery) { + content.push(`in ${this.store.cancerTypeName}`); + } + return getPageTitle(content.join(' ')); + } + + @computed + get geneticType() { + const { geneticType } = parseAlterationPagePath( + this.props.location.pathname + ); + return geneticType ?? GENETIC_TYPE.SOMATIC; + } + + onChangeTumorType(newTumorType: string) { + this.store.tumorTypeQuery = newTumorType; + } + + getImplications(evidences: Evidence[]) { + return evidences.reduce((acc, evidence) => { + const level = levelOfEvidence2Level(evidence.levelOfEvidence); + const fdaLevel = levelOfEvidence2Level(evidence.fdaLevel); + const alterations = evidence.alterations.filter(alteration => + alteration.referenceGenomes.includes(this.store.referenceGenomeQuery) + ); + const alterationsName = alterations + .map(alteration => alteration.name) + .join(', '); + const cancerTypes = evidence.cancerTypes.map(cancerType => + getCancerTypeNameFromOncoTreeType(cancerType) + ); + const excludedCancerTypes = evidence.excludedCancerTypes.map(ct => + getCancerTypeNameFromOncoTreeType(ct) + ); + const cancerTypesName = getCancerTypesName( + cancerTypes, + excludedCancerTypes + ); + if (evidence.treatments.length > 0) { + evidence.treatments.forEach(treatment => { + acc.push({ + level, + fdaLevel, + drugDescription: evidence.description, + alterations: alterationsName, + alterationsView: ( + + {alterations.map(alteration => + alteration.consequence ? ( + + ) : ( + {alteration.name} + ) + )} + + ), + drugs: getTreatmentNameByPriority(treatment), + cancerTypes: cancerTypesName, + cancerTypesView: ( + <> + + {cancerTypes.map(cancerType => ( + + {cancerType} + + ))} + + {excludedCancerTypes.length > 0 ? ( + (excluding {excludedCancerTypes.join(', ')}) + ) : ( + <> + )} + + ), + citations: articles2Citations(evidence.articles), + } as TherapeuticImplication); + }); + } else { + acc.push({ + level, + fdaLevel, + drugDescription: evidence.description, + alterations: alterationsName, + alterationsView: ( + + {alterations.map(alteration => + alteration.consequence ? ( + + ) : ( + {alteration.name} + ) + )} + + ), + drugs: '', + cancerTypes: cancerTypesName, + cancerTypesView: ( + <> + + {cancerTypes.map(cancerType => ( + + {cancerType} + + ))} + + {excludedCancerTypes.length > 0 ? ( + (excluding {excludedCancerTypes.join(', ')}) + ) : ( + <> + )} + + ), + citations: articles2Citations(evidence.articles), + } as TherapeuticImplication); + } + return acc; + }, [] as TherapeuticImplication[]); + } + + getEvidenceByEvidenceTypes( + cancerTypes: VariantAnnotationTumorType[], + evidenceTypes: EVIDENCE_TYPES[] + ): Evidence[] { + let uniqueEvidences: Evidence[] = []; + cancerTypes.forEach(cancerType => { + uniqueEvidences = uniqueEvidences.concat( + cancerType.evidences.filter(evidence => + evidenceTypes.includes(evidence.evidenceType as EVIDENCE_TYPES) + ) + ); + }); + + return uniqBy(uniqueEvidences, evidence => evidence.id); + } + + @computed + get therapeuticImplications(): TherapeuticImplication[] { + return this.getImplications( + this.getEvidenceByEvidenceTypes( + this.store.annotationData.result.tumorTypes, + TREATMENT_EVIDENCE_TYPES + ) + ); + } + + @computed + get fdaImplication(): FdaImplication[] { + const evidences = this.getEvidenceByEvidenceTypes( + this.store.annotationData.result.tumorTypes, + TREATMENT_EVIDENCE_TYPES + ); + const fdaImplications: FdaImplication[] = []; + evidences.forEach(evidence => { + const level = levelOfEvidence2Level(evidence.levelOfEvidence); + const fdaLevel = levelOfEvidence2Level(evidence.fdaLevel); + const alterations = evidence.alterations.filter(alteration => + alteration.referenceGenomes.includes(this.store.referenceGenomeQuery) + ); + alterations.forEach(alt => { + // convert all alterations to matchedAlteration/alteration query if not positional variant + let mappedAlteration = {} as Alteration; + if ( + this.store.relevantAlterations.result + .map(relevantAlt => relevantAlt.alteration) + .includes(alt.alteration) + ) { + mappedAlteration = alt; + } else { + if (this.store.alteration.result) { + mappedAlteration = this.store.alteration.result; + } else { + mappedAlteration.name = mappedAlteration.alteration = this.store.alterationName; + } + } + const ctNames = evidence.cancerTypes.map(ct => + getCancerTypeNameFromOncoTreeType(ct) + ); + const excludedCtNames = evidence.excludedCancerTypes.map(ct => + getCancerTypeNameFromOncoTreeType(ct) + ); + fdaImplications.push({ + level: fdaLevel, + alteration: mappedAlteration, + alterationView: ( + + ), + cancerType: getCancerTypesName(ctNames, excludedCtNames), + cancerTypeView: ( + <> + + {ctNames.map(cancerType => ( + + {cancerType} + + ))} + + {excludedCtNames.length > 0 ? ( + (excluding {excludedCtNames.join(', ')}) + ) : ( + <> + )} + + ), + }); + }); + }); + return getUniqueFdaImplications(fdaImplications); + } + + @computed + get diagnosticImplications(): TherapeuticImplication[] { + return this.getImplications( + this.getEvidenceByEvidenceTypes( + this.store.annotationData.result.tumorTypes, + [EVIDENCE_TYPES.DIAGNOSTIC_IMPLICATION] + ) + ); + } + + @computed + get prognosticImplications(): TherapeuticImplication[] { + return this.getImplications( + this.getEvidenceByEvidenceTypes( + this.store.annotationData.result.tumorTypes, + [EVIDENCE_TYPES.PROGNOSTIC_IMPLICATION] + ) + ); + } + + @computed + get alterationSummaries() { + const orderedSummaries = [SummaryKey.GENE_SUMMARY]; + if (!this.isCategoricalAlteration) { + orderedSummaries.push(SummaryKey.ALTERATION_SUMMARY); + } + return getSummaries(this.store.annotationData.result, orderedSummaries); + } + + @computed get isCategoricalAlteration() { + return isCategoricalAlteration(this.store.alterationName); + } + + @autobind + onChangeTab( + selectedTabKey: ANNOTATION_PAGE_TAB_KEYS, + newTabKey: ANNOTATION_PAGE_TAB_KEYS + ) { + if (newTabKey === ANNOTATION_PAGE_TAB_KEYS.FDA) { + this.props.appStore.inFdaRecognizedContent = true; + } + if ( + selectedTabKey === ANNOTATION_PAGE_TAB_KEYS.FDA && + newTabKey !== ANNOTATION_PAGE_TAB_KEYS.FDA + ) { + this.props.appStore.showFdaModal = true; + } else { + const newHash: AlterationPageHashQueries = { tab: newTabKey }; + window.location.hash = QueryString.stringify(newHash); + } + this.selectedTab = newTabKey; + } + + render() { + const headerText = ( +
+ {this.store.hugoSymbol} {this.store.alterationNameWithDiff}{' '} + + {upperFirst(this.geneticType)} + +
+ ); + return ( + <> + + {this.documentTitle} + + + {this.pageShouldBeRendered ? ( + <> + + + +
+ + + Back to {this.store.hugoSymbol} + +
+

{headerText}

+ + + {this.store.annotationData.result.mutationEffect + .description && ( + + + + } + onClick={() => + this.toggleMutationEffect(!this.showMutationEffect) + } + /> + + + )} + +
+
+ + {headerText} + + } + /> + + + +
+ {this.store.germline && ( + <> + + + + )} + + + +
+ ), + }, + { + title: 'Diagnostic', + value: ( + + ), + }, + ], + [ + { + title: 'Prognostic', + value: ( + + ), + }, + { + title: 'FDA', + value: ( + + ), + }, + ], + ]} + /> + + +
+ + + + Genomic Indicators + + + + Clinical Implications + + {this.store.cancerTypeName ? ( + + ) : ( + + )} + + +
+ + ) : ( + + + + An error occurred while annotating your variant. + + + + + + + )} + + ); + } +} diff --git a/src/main/webapp/app/routes/routes.tsx b/src/main/webapp/app/routes/routes.tsx index b95a37211..a62238256 100644 --- a/src/main/webapp/app/routes/routes.tsx +++ b/src/main/webapp/app/routes/routes.tsx @@ -37,6 +37,7 @@ import { NewsPageNavTab } from 'app/pages/newsPage/NewsPageNavTab'; import CompanionDiagnosticDevicePage from 'app/pages/companionDiagnosticDevicesPage/companionDiagnosticDevicePage'; import OncokbRoute from 'app/shared/route/OncokbRoute'; import GenePage from 'app/pages/genePage/GenePage'; +import { SomaticGermlineAlterationPage } from 'app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage'; const getOldLevelsRedirectRoute = (hash: string) => { const queryStrings = QueryString.parse(hash) as { @@ -192,6 +193,20 @@ const AppRoutes = (props: { path={PAGE_ROUTE.ALTERATION} component={AlterationPage} /> + + { const linkoutAltName = getCategoricalAlteration( typeof props.alteration === 'string' @@ -96,7 +97,12 @@ export const getAlterationPageLink = (props: { : props.alteration.name ); - let pageLink = `${PAGE_ROUTE.GENE_HEADER}/${props.hugoSymbol}/${linkoutAltName}`; + const geneTypePath = + props.germline !== undefined + ? `/${props.germline ? 'germline' : 'somatic'}` + : ''; + + let pageLink = `${PAGE_ROUTE.GENE_HEADER}/${props.hugoSymbol}${geneTypePath}/${linkoutAltName}`; if (props.cancerType) { pageLink = `${pageLink}/${encodeSlash(props.cancerType)}`; } @@ -202,6 +208,28 @@ export const parseGenePagePath = (pathname: string) => { } }; +export type AlterationPagePath = { + hugoSymbol?: string; + geneticType?: GENETIC_TYPE; +}; + +export const parseAlterationPagePath = (pathname: string) => { + const startsWithGene = pathname.startsWith(PAGE_ROUTE.GENE_HEADER); + const inExtendedGenePage = (pathname.match(/\//g) || []).length === 4; + if (startsWithGene && inExtendedGenePage) { + const segments = pathname.split('/') || []; + const result: GenePagePath = { + hugoSymbol: segments[1], + }; + if (segments.length > 3) { + result.geneticType = segments[3] as GENETIC_TYPE; + } + return result; + } else { + return {}; + } +}; + export const getGenomicPageLocation = (props: { rootRoute: PAGE_ROUTE.GENOMIC_CHANGE | PAGE_ROUTE.HGVSG; query: string; diff --git a/src/main/webapp/app/shared/utils/Utils.tsx b/src/main/webapp/app/shared/utils/Utils.tsx index eea79422d..f7dd578ac 100644 --- a/src/main/webapp/app/shared/utils/Utils.tsx +++ b/src/main/webapp/app/shared/utils/Utils.tsx @@ -142,7 +142,7 @@ export function toggleStrList(element: string, list: string[]) { return list; } -export function level2LevelOfEvidence(level: LEVELS) { +export function level2LevelOfEvidence(level: LEVELS | undefined) { switch (level) { case LEVELS.Tx3: case LEVELS.Tx3A: @@ -314,7 +314,7 @@ export const OncoKBLevelIcon: React.FunctionComponent<{ }; export const FdaLevelIcon: React.FunctionComponent<{ - level: LEVELS; + level: LEVELS | undefined; withDescription?: boolean; size?: 's1' | 's2' | 's3'; }> = ({ level, withDescription = true, size = 's1' }) => { @@ -324,11 +324,14 @@ export const FdaLevelIcon: React.FunctionComponent<{ lineHeight: `${16 * scale}px`, margin: '0 3px', }; + if (level === undefined) { + return <>; + } const fdaIcon = ( - {level.toString().replace('Fda', '')} + {level?.toString().replace('Fda', '')} );