diff --git a/package.json b/package.json index ff7fdf53..70726c95 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "version": "0.1.0", "private": false, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@playwright/test": "1.30.0", "@types/file-saver": "2.0.5", "@types/react": "18.2.74", "@types/react-dom": "18.2.23", "@types/react-router-dom": "5.3.3", - "@types/uuid": "^8.3.1", + "@types/uuid": "9.0.8", "axios": "1.3.2", "axios-cache-interceptor": "1.0.1", "balloon-css": "^1.2.0", @@ -17,19 +18,19 @@ "file-saver": "^2.0.5", "pdbe-molstar": "3.1.2", "react": "18.2.0", + "react-cookie-consent": "9.0.0", "react-dom": "18.2.0", "react-dropdown-now": "^6.0.1", + "react-markdown": "^8.0.7", "react-router-dom": "6.22.3", "react-scripts": "4.0.3", + "rehype-raw": "6.1.1", + "remark-gfm": "3.0.1", "simple-react-notifications2": "1.2.17", + "tinygradient": "1.1.5", "typescript": "4.9.5", - "uuid": "3.4.0", - "web-vitals": "2.1.4", - "react-markdown": "^8.0.7", - "remark-gfm": "3.0.1", - "rehype-raw": "6.1.1", - "react-cookie-consent": "9.0.0", - "@babel/plugin-proposal-private-property-in-object": "7.21.11" + "uuid": "9.0.1", + "web-vitals": "2.1.4" }, "scripts": { "start": "react-scripts start", diff --git a/src/constants/ExternalUrls.ts b/src/constants/ExternalUrls.ts index 467ad583..f5134cf7 100644 --- a/src/constants/ExternalUrls.ts +++ b/src/constants/ExternalUrls.ts @@ -11,6 +11,7 @@ export const RHEA_URL = "https://www.rhea-db.org/rhea/"; export const INTACT_URL = "https://www.ebi.ac.uk/intact/search?query=" export const UNIPROT_ACCESSION_URL = "https://www.uniprot.org/uniprot/" export const CADD_INFO_URL = "https://cadd.gs.washington.edu/info" +export const AM_INFO_URL = "https://alphamissense.hegelab.org/" export const VCF_FORMAT_INFO_URL = "https://github.com/ebi-uniprot/protvar-be/blob/main/docs/vcf-format.md" export const DBSNP_URL = "https://www.ncbi.nlm.nih.gov/snp/" export const CLINVAR_RCV_URL = "https://www.ncbi.nlm.nih.gov/clinvar/" diff --git a/src/index.tsx b/src/index.tsx index a2a2b7de..3a6886ab 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,18 +1,18 @@ -import * as React from "react"; -import { createRoot } from "react-dom/client"; +import React from "react"; +import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import './styles/index.scss'; import reportWebVitals from './reportWebVitals'; import App from './ui/App'; -const container = document.getElementById('root'); -if (!container) throw new Error('Failed to find the root element'); -const root = createRoot(container); +const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); root.render( - - - + + + + + ); // If you want to start measuring performance in your app, pass a function diff --git a/src/styles/base/_variables.scss b/src/styles/base/_variables.scss index cf8a318a..6095eb06 100644 --- a/src/styles/base/_variables.scss +++ b/src/styles/base/_variables.scss @@ -44,8 +44,6 @@ $search-results-badge-border-colour: #333; $search-results-badge-colour: #333; $novel-variant-colour: #f00; $non-coding-variant-colour: #999; -$cadd-score-green-background-colour: #6dab49; -$cadd-score-red-background-colour: #da0f21; $significance-data-block-border-colour: #ddd; $publications-label-background-colour: #ffb100; $input-example-border-colour: #ffb100; diff --git a/src/styles/search/_ImpactSearchResults.scss b/src/styles/search/_ImpactSearchResults.scss index 90996958..eb99bbf4 100644 --- a/src/styles/search/_ImpactSearchResults.scss +++ b/src/styles/search/_ImpactSearchResults.scss @@ -73,14 +73,6 @@ border-style: none; height: 100%; } - - .cadd-score { - width: 2rem; - text-align: center; - display: inline-block; - margin-right: 1rem; - margin-bottom: 0.3rem; - } } .img-table { @@ -443,37 +435,6 @@ vertical-align: top; } - .cadd-score { - padding: 0.3rem 0.4rem; - border-radius: 0.75rem; - font-size: 0.8rem; - font-weight: 700; - - &--DarkGreen { - background-color: DarkGreen; - } - - &--DarkSeaGreen { - color: $white-colour; - background-color: DarkSeaGreen; - } - - &--Gold { - color: $white-colour; - background-color:Gold; - } - - &--DarkOrange { - color: $white-colour; - background-color: DarkOrange; - } - - &--FireBrick { - color: $white-colour; - background-color: FireBrick; - } - } - .expanded-row { background-color: #CDD9E4; } @@ -689,16 +650,35 @@ padding-left: 1rem; } - .eve-benign { - color: blue; + .esm1b-score-grad { + background-color: #460556; + background-image: linear-gradient(to right, #460556, #218c8f, #f9e725); + width: 8rem; + height: 1.5rem; } - - .eve-pathogenic { - color: red; + .esm1b-score-grad-std { + background-color: red; + background-image: linear-gradient(to right, blue, lightgray, red); + width: 8rem; + height: 1.5rem; + } + .score-label { + text-align: justify; + width: 8rem; + height: 1.5rem; + font-size: 0.7em; + } + .score-label:after { + content: ""; + display: inline-block; + width: 100%; } - .eve-uncertain { - color: lightgrey; + .conserv-score-grad { + background-color: #732faf; + background-image: linear-gradient(to right, #732faf, #194888, #277777, #72cb5d, #bab518, #c46307, #9d0101); + width: 8rem; + height: 1.5rem; } } @@ -776,3 +756,24 @@ .pae-desc { padding-left: 20px; } + +.aa-pred{ + display: grid; + grid-template-columns: 35% 20% auto; +} + +.info { + display: inline-block; + padding-left: 4px; + padding-right: 4px; +} +.info::before { + content: 'i'; + font-style: italic; + vertical-align: super; + font-size: smaller; +} +.info:hover { + cursor: help; +} + diff --git a/src/types/FunctionalResponse.ts b/src/types/FunctionalResponse.ts index d4e18528..1471ac19 100644 --- a/src/types/FunctionalResponse.ts +++ b/src/types/FunctionalResponse.ts @@ -17,7 +17,6 @@ export interface FunctionalResponse { pockets: Array foldxs: Array interactions: Array - conservScore: number } export interface GeneName { @@ -81,7 +80,7 @@ export interface Foldx { position: number wildType: string mutatedType: string - foldxDdq: number + foldxDdg: number plddt: number } diff --git a/src/types/MappingResponse.ts b/src/types/MappingResponse.ts index cb6c4607..3f433372 100644 --- a/src/types/MappingResponse.ts +++ b/src/types/MappingResponse.ts @@ -118,8 +118,10 @@ interface IsoFormMapping { // evolutionalInferenceUri: string; // proteinStructure: Array; proteinStructureUri: string; - eveScore: number; - eveClass: number; + conservScore: ConservScore; + amScore: AMScore; + eveScore: EVEScore; + esmScore: ESMScore; } interface Ensp { ensp: string; @@ -139,4 +141,23 @@ export interface ParsedInput{ inputString: string invalidReason: string } + +export interface ConservScore { + score:number +} + +export interface EVEScore { + score:number + eveClass:string +} + +export interface ESMScore { + score:number +} + +export interface AMScore { + amPathogenicity:number + amClass:string +} + export default MappingResponse; \ No newline at end of file diff --git a/src/ui/App.tsx b/src/ui/App.tsx index b3fe8fca..69974522 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -1,4 +1,4 @@ -import {useState} from "react"; +import React, {createContext, useState} from "react"; import {useNavigate, Route, Routes} from "react-router-dom"; import HomePage from "./pages/home/HomePage"; import SearchResultsPage from "./pages/search/SearchResultPage"; @@ -18,13 +18,19 @@ import DownloadPage from "./pages/download/DownloadPage"; import HelpPage from "./pages/help/HelpPage"; import {FormData, initialFormData} from "../types/FormData"; +export const StdColorContext = createContext(true); export default function App() { + const [stdColor, setStdColor] = useState(true); const [loading, setLoading] = useState(false); const [formData, setFormData] = useState(initialFormData); const [page, setPage] = useState(firstPage(0)); const [searchResults, setSearchResults] = useState([]); const navigate = useNavigate(); + const toggleStdColor = () => { + setStdColor(stdColor ? false : true); + }; + // MappingRecord 3d array -> [][][] list of mappings/genes/isoforms // mappings : [ // ... @@ -152,7 +158,7 @@ export default function App() { .finally(() => setLoading(false)); } - return ( + return ( } /> - } /> + } /> } /> } /> } /> @@ -181,5 +188,6 @@ export default function App() { } /> } /> + ); } diff --git a/src/ui/components/common/Common.tsx b/src/ui/components/common/Common.tsx new file mode 100644 index 00000000..480f083e --- /dev/null +++ b/src/ui/components/common/Common.tsx @@ -0,0 +1,10 @@ +export function Info(props: {text?: string}) { + if (props.text === null) + return <> + return
+} + +export const pubmedRef = (id: number) => { + return ref +} \ No newline at end of file diff --git a/src/ui/components/function/FunctionalDataRow.tsx b/src/ui/components/function/FunctionalDataRow.tsx index 8ef92ef8..7a015b5c 100644 --- a/src/ui/components/function/FunctionalDataRow.tsx +++ b/src/ui/components/function/FunctionalDataRow.tsx @@ -3,31 +3,28 @@ import ProteinFunctionTable from './ProteinFunctionTable'; import GeneAndTranslatedSequenceTable from './GeneAndTranslatedSequenceTable'; import ProteinInformationTable from './ProteinInformationTable'; import ResidueRegionTable from './ResidueRegionTable'; -import { TranslatedSequence } from '../../../utills/Convertor'; +import {MappingRecord} from '../../../utills/Convertor'; import ProteinIcon from '../../../images/proteins.svg'; import {FunctionalResponse} from "../../../types/FunctionalResponse"; interface FunctionalDataRowProps { - apiData: FunctionalResponse - refAA: string - variantAA: string - ensg: string - ensp: Array + functionalData: FunctionalResponse + record: MappingRecord } function FunctionalDataRow(props: FunctionalDataRowProps) { - const { refAA, variantAA, ensg, ensp, apiData } = props; + const { functionalData, record } = props; return (
-
protein icon Reference Function
- - - - +
protein icon Functional information
+ + + +
diff --git a/src/ui/components/function/FunctionalDetail.tsx b/src/ui/components/function/FunctionalDetail.tsx index 6d4c9786..cebea13c 100644 --- a/src/ui/components/function/FunctionalDetail.tsx +++ b/src/ui/components/function/FunctionalDetail.tsx @@ -2,21 +2,18 @@ import { useState, useEffect } from 'react'; import NoFunctionalDataRow from './NoFunctionalDataRow'; import FunctionalDataRow from './FunctionalDataRow'; import LoaderRow from '../search/LoaderRow'; -import { TranslatedSequence } from '../../../utills/Convertor'; +import {MappingRecord} from '../../../utills/Convertor'; import {getFunctionalData} from "../../../services/ProtVarService"; import {FunctionalResponse} from "../../../types/FunctionalResponse"; interface FunctionalDetailProps { referenceFunctionUri: string - refAA: string - variantAA: string - ensg: string - ensp: Array + record: MappingRecord } function FunctionalDetail(props: FunctionalDetailProps) { - const { referenceFunctionUri } = props; + const { referenceFunctionUri, record } = props; const [apiData, setApiData] = useState() useEffect(() => { getFunctionalData(referenceFunctionUri).then( @@ -28,7 +25,7 @@ function FunctionalDetail(props: FunctionalDetailProps) { if (!apiData) return else if (apiData.id) - return + return else return ; } diff --git a/src/ui/components/function/ResidueRegionTable.tsx b/src/ui/components/function/ResidueRegionTable.tsx index e8d17c96..d3b1873e 100644 --- a/src/ui/components/function/ResidueRegionTable.tsx +++ b/src/ui/components/function/ResidueRegionTable.tsx @@ -1,29 +1,33 @@ -import { useState, Fragment } from "react"; -import { EmptyElement } from "../../../constants/ConstElement"; -import { FEATURES } from "../../../constants/Protein"; +import {useState, Fragment} from "react"; +import {EmptyElement} from "../../../constants/ConstElement"; +import {FEATURES} from "../../../constants/Protein"; import AminoAcidModel from "./AminoAcidModel"; import Evidences from "./Evidences"; -import { ReactComponent as ChevronDownIcon } from "../../../images/chevron-down.svg" -import { v1 as uuidv1 } from 'uuid'; -import { StringVoidFun } from "../../../constants/CommonTypes"; +import {ReactComponent as ChevronDownIcon} from "../../../images/chevron-down.svg" +import {v1 as uuidv1} from 'uuid'; +import {StringVoidFun} from "../../../constants/CommonTypes"; import {aminoAcid3to1Letter, formatRange, getKeyValue} from "../../../utills/Util"; import {FunctionalResponse, Pocket, Foldx, P2PInteraction, ProteinFeature} from "../../../types/FunctionalResponse"; +import {MappingRecord} from "../../../utills/Convertor"; +import {Prediction, PUBMED_ID} from "./prediction/Prediction"; +import {pubmedRef} from "../common/Common"; interface ResidueRegionTableProps { - apiData: FunctionalResponse - refAA: string - variantAA: string + functionalData: FunctionalResponse + record: MappingRecord } + function ResidueRegionTable(props: ResidueRegionTableProps) { const [expendedRowKey, setExpendedRowKey] = useState('') + function toggleRow(key: string) { setExpendedRowKey(expendedRowKey === key ? '' : key) } var regions: Array = []; var residues: Array = []; - if (props.apiData.features && props.apiData.features.length > 0) { - props.apiData.features.forEach((feature) => { + if (props.functionalData.features && props.functionalData.features.length > 0) { + props.functionalData.features.forEach((feature) => { if (feature.category !== 'VARIANTS') { if (feature.begin === feature.end) residues.push(feature); @@ -31,80 +35,62 @@ function ResidueRegionTable(props: ResidueRegionTableProps) { regions.push(feature); } }); - const oneLetterVariantAA = aminoAcid3to1Letter(props.variantAA); + const oneLetterVariantAA = aminoAcid3to1Letter(props.record.variantAA!); return - - - - - - - - + + + + + + + +
Variant Residue PositionRegion Containing Variant Position
{getResidues(residues, props.apiData.conservScore, props.apiData.foldxs, props.refAA, props.variantAA, oneLetterVariantAA, expendedRowKey, toggleRow)}{getRegions(regions, props.apiData.accession, props.apiData.pockets, props.apiData.interactions, expendedRowKey, toggleRow)}
Variant Residue PositionRegion Containing Variant Position
{getResidues(residues, props.record, props.functionalData.foldxs, oneLetterVariantAA, expendedRowKey, toggleRow)}{getRegions(regions, props.functionalData.accession, props.functionalData.pockets, props.functionalData.interactions, expendedRowKey, toggleRow)}
} return EmptyElement } -function getResidues(regions: Array, conservScore: number, foldxs: Array, refAA: string, variantAA: string, oneLetterVariantAA: string | null, expendedRowKey: string, toggleRow: StringVoidFun) { - let regionsList: Array = []; - let counter = 0; - let foldxs_ = oneLetterVariantAA ? foldxs.filter(foldx => foldx.mutatedType.toLowerCase() === oneLetterVariantAA) : foldxs - - if (regions.length === 0) { - return <> - - Conservation score: {conservScore}
- - ; - } - regions.forEach((region) => { - counter = counter + 1; - let key = 'residue-' + counter; - var list = getFeatureList(region, key, expendedRowKey, toggleRow); - regionsList.push(list); - }); +function getResidues(regions: Array, record: MappingRecord, foldxs: Array, oneLetterVariantAA: string | null, expendedRowKey: string, toggleRow: StringVoidFun) { + let foldxs_ = oneLetterVariantAA ? foldxs.filter(foldx => foldx.mutatedType.toLowerCase() === oneLetterVariantAA) : foldxs return <> - - Conservation score: {conservScore}
- {regionsList} - + Annotations from UniProt + {regions.length === 0 &&
+ No functional data for the variant position +
+ } + { + regions.map((region, idx) => { + return getFeatureList(region, `residue-${idx}`, expendedRowKey, toggleRow); + }) + } + + } function getRegions(regions: Array, accession: string, pockets: Array, interactions: Array, expendedRowKey: string, toggleRow: StringVoidFun) { - let regionsList: Array = []; - let counter = 0; - - if (regions.length === 0) { - return (<> - -

- Predictions -
(Source: PubMed ID 15980494)
- - - ); - } - regions.forEach((region) => { - counter = counter + 1; - let key = 'region-' + counter; - var list = getFeatureList(region, key, expendedRowKey, toggleRow); - regionsList.push(list); - }); - return <> - Curated observations from UniProt - {regionsList} -

- Predictions -
(Source: PubMed ID 15980494)
- - - + return (<> + Annotations from UniProt + {regions.length === 0 &&
+ No functional data for the region +
+ } + { + regions.map((region, idx) => { + return getFeatureList(region, `region-${idx}`, expendedRowKey, toggleRow); + }) + } + { + (pockets.length > 0 || interactions.length >0) && <> + Structure predictions{pubmedRef(PUBMED_ID.INTERFACES)} + + } + + + ); } function getFeatureList(feature: ProteinFeature, key: string, expendedRowKey: string, toggleRow: StringVoidFun) { @@ -120,7 +106,7 @@ function getFeatureList(feature: ProteinFeature, key: string, expendedRowKey: st return {getFeatureDetail(key, feature, expendedRowKey)} @@ -130,11 +116,11 @@ function getFeatureDetail(rowKey: string, feature: ProteinFeature, expendedRowKe if (rowKey === expendedRowKey) { return ( <> -
    +
    • {getPositionLabel(feature.begin, feature.end)} -
      - +
      +
    @@ -149,40 +135,6 @@ function getPositionLabel(begin: number, end: number) { return <>Range : {begin} - {end} } -interface FoldxPredProps { - foldxs: Array - expendedRowKey: string - toggleRow: StringVoidFun -} - -const FoldxPred = (props: FoldxPredProps) => { - if (!props.foldxs || props.foldxs.length === 0) { - return <> - } - let key = 'foldxs-0' - return - - {getFoldxDetail(props.foldxs, key, props.expendedRowKey)} - -} - -function getFoldxDetail(foldxs: Array, rowKey: string, expendedRowKey: string) { - if (rowKey === expendedRowKey) { - return
      -
    • - ΔΔGpred : {foldxs[0].foldxDdq} -
      - pLDDT : {foldxs[0].plddt} -
      - (Source: PubMed ID 15980494) -
    • -
    - } -} - interface PocketsProps { pockets: Array expendedRowKey: string @@ -195,13 +147,13 @@ const Pockets = (props: PocketsProps) => { props.pockets.forEach((pocket) => { counter = counter + 1; - let key = 'pockets-'+counter + let key = 'pockets-' + counter pocketsList.push(
  • - Energy : {pocket.energy}
    - Energy/vol : {pocket.energyPerVol}
    - Score : {pocket.score}
    - Resid : {formatRange(pocket.residList)} -
  • ); + Energy : {pocket.energy}
    + Energy/vol : {pocket.energyPerVol}
    + Score : {pocket.score}
    + Resid : {formatRange(pocket.residList)} + ); }); if (pocketsList.length === 0) return <>; @@ -210,7 +162,7 @@ const Pockets = (props: PocketsProps) => { return {getList(pocketsList, key, props.expendedRowKey)} @@ -229,7 +181,7 @@ const Interfaces = (props: InterfacesProps) => { props.interactions.forEach((interaction) => { counter = counter + 1; - let key = 'interfaces-'+counter + let key = 'interfaces-' + counter let chain = 'A' let pair = interaction.a @@ -251,7 +203,7 @@ const Interfaces = (props: InterfacesProps) => { return {getList(interfacesList, key, props.expendedRowKey)} @@ -259,14 +211,10 @@ const Interfaces = (props: InterfacesProps) => { function getList(list: Array, rowKey: string, expendedRowKey: string) { if (rowKey === expendedRowKey) { - return
      + return
        {list}
      } } -/* -const NoData = () => { - return -}*/ export default ResidueRegionTable; \ No newline at end of file diff --git a/src/ui/components/function/prediction/AlphaMissensePred.tsx b/src/ui/components/function/prediction/AlphaMissensePred.tsx new file mode 100644 index 00000000..ff91d93f --- /dev/null +++ b/src/ui/components/function/prediction/AlphaMissensePred.tsx @@ -0,0 +1,40 @@ +import {AMScore} from "../../../../types/MappingResponse"; +import {PredAttr, PUBMED_ID} from "./Prediction"; +import Spaces from "../../../elements/Spaces"; +import {STD_BENIGN_COLOR, STD_PATHOGENIC_COLOR, STD_UNCERTAIN_COLOR} from "./PredConstants"; +import {pubmedRef} from "../../common/Common"; + +export const AM_SCORE_ATTR: {[key: string]: PredAttr} = { + BENIGN: { color: '#3853a4', stdColor: STD_BENIGN_COLOR, title: 'benign' }, + AMBIGUOUS: { color: '#a8a9ad', stdColor: STD_UNCERTAIN_COLOR, title: 'ambiguous' }, + PATHOGENIC: { color: '#ed1e24', stdColor: STD_PATHOGENIC_COLOR, title: 'pathogenic' } +} + +export const AlphaMissensePred = (props: { am?: AMScore, stdColor: boolean }) => { + if (props.am === undefined || props.am === null) { + return <> + } + return
      +
      AlphaMissense {pubmedRef(PUBMED_ID.AM)}
      +
      {props.am.amPathogenicity}
      +
      +
      +} + +function AlphaMissensePredIcon(props: { am?: AMScore, stdColor: boolean }) { + if (props.am) { + let cls = props.am.amClass as keyof PredAttr + return <> + + {AM_SCORE_ATTR[cls].title} + + } + return <> +} + +export function amScoreAttr(amClass?: string) { + if (amClass === undefined || amClass === null || !(amClass in AM_SCORE_ATTR)) { + return null; + } + return AM_SCORE_ATTR[amClass] +} \ No newline at end of file diff --git a/src/ui/components/function/prediction/ConservPred.tsx b/src/ui/components/function/prediction/ConservPred.tsx new file mode 100644 index 00000000..da5ad241 --- /dev/null +++ b/src/ui/components/function/prediction/ConservPred.tsx @@ -0,0 +1,61 @@ +import tinygradient from "tinygradient"; +import {ConservScore} from "../../../../types/MappingResponse"; +import {PredAttr, PUBMED_ID} from "./Prediction"; +import Spaces from "../../../elements/Spaces"; +import {Info, pubmedRef} from "../../common/Common"; + +export const CONSERV_SCORE_ATTR: PredAttr[] = [ + {color: '#732faf', stdColor: '', title: 'very low' }, + {color: '#194888', stdColor: '', title: 'low' }, + {color: '#277777', stdColor: '', title: 'fairly low' }, + {color: '#72cb5d', stdColor: '', title: 'moderate' }, + {color: '#bab518', stdColor: '', title: 'fairly high' }, + {color: '#c46307', stdColor: '', title: 'high' }, + {color: '#9d0101', stdColor: '', title: 'very high' } +] + +export const CONSERV_COLOUR_GRADIENT = tinygradient(CONSERV_SCORE_ATTR.map(s => s.color)); + +export const ConservPred = (props: { conserv?: ConservScore }) => { + if (props.conserv === undefined || props.conserv === null) { + return <> + } + return
      +
      Conservation {pubmedRef(PUBMED_ID.CONSERV)}
      +
      {props.conserv.score} + +
      +
      +
      +} + +function ConservPredIcon(props: { conservScore?: ConservScore }) { + if (props.conservScore) { + const scoreAttr = conservScoreAttr(props.conservScore.score) + const colorAtPos = CONSERV_COLOUR_GRADIENT.rgbAt(props.conservScore.score).toHexString() + return <> + + {scoreAttr.title} + + + } + return <> +} + +export function conservScoreAttr(score: number) { + let idx: number = 0 + if (score >= 0.15 && score < 0.3) { + idx = 1 + } else if (score >= 0.3 && score < 0.45) { + idx = 2 + } else if (score >= 0.45 && score < 0.6) { + idx = 3 + } else if (score >= 0.6 && score < 0.75) { + idx = 4 + } else if (score >= 0.75 && score < 0.9) { + idx = 5 + } else if (score >= 0.9) { + idx = 6 + } + return CONSERV_SCORE_ATTR[idx]; +} \ No newline at end of file diff --git a/src/ui/components/function/prediction/EsmPred.tsx b/src/ui/components/function/prediction/EsmPred.tsx new file mode 100644 index 00000000..3678197f --- /dev/null +++ b/src/ui/components/function/prediction/EsmPred.tsx @@ -0,0 +1,58 @@ +import tinygradient from "tinygradient"; +import {ESMScore} from "../../../../types/MappingResponse"; +import { + PredAttr, + PUBMED_ID +} from "./Prediction"; +import Spaces from "../../../elements/Spaces"; +import {STD_BENIGN_COLOR, STD_COLOR_GRADIENT, STD_PATHOGENIC_COLOR, STD_UNCERTAIN_COLOR} from "./PredConstants"; +import {Info, pubmedRef} from "../../common/Common"; + +// likely pathogenic (yellow) -25 <------> 0 likely benign (blue) +export const ESM_SCORE_ATTR: PredAttr[] = [ + {color: '#460556', stdColor: STD_BENIGN_COLOR , title: 'benign', info: '-5 to 0 likely benign' }, + {color: '#218c8f', stdColor: STD_UNCERTAIN_COLOR, title: 'uncertain', info: '-10 to -5 uncertain significance' }, + {color: '#f9e725', stdColor: STD_PATHOGENIC_COLOR, title: 'pathogenic', info: '-25 to -10 likely pathogenic' } +] + +export const ESM_MAX_SCORE = -25 + +export const ESM_COLOR_GRADIENT = tinygradient(ESM_SCORE_ATTR.map(s => s.color)); + +export const EsmPred = (props: { esm?: ESMScore, stdColor: boolean }) => { + if (props.esm === undefined || props.esm === null) { + return <> + } + return
      +
      ESM-1b {pubmedRef(PUBMED_ID.ESM)}
      +
      {props.esm.score}
      +
      +
      +} + +function EsmPredIcon(props: {esm?: ESMScore, stdColor: boolean }) { + if (props.esm) { + const colorGrad = props.stdColor ? STD_COLOR_GRADIENT : ESM_COLOR_GRADIENT; + const colorAtPos = colorGrad.rgbAt(props.esm.score/ ESM_MAX_SCORE).toHexString() + const esmAttr = esmScoreAttr(props.esm.score) + return <> + + {esmAttr && <> + {esmAttr.title} + + } + + } + return <> +} + +function esmScoreAttr(score: number) { + if (score >= -5 && score < 0) { + return ESM_SCORE_ATTR[0] + } else if (score >= -10 && score < -5) { + return ESM_SCORE_ATTR[1] + } else if (score >= -25 && score < -10) { + return ESM_SCORE_ATTR[2] + } + return null +} \ No newline at end of file diff --git a/src/ui/components/function/prediction/EvePred.tsx b/src/ui/components/function/prediction/EvePred.tsx new file mode 100644 index 00000000..48c2b58e --- /dev/null +++ b/src/ui/components/function/prediction/EvePred.tsx @@ -0,0 +1,36 @@ +import {EVEScore} from "../../../../types/MappingResponse"; +import { + PredAttr, + PUBMED_ID +} from "./Prediction"; +import Spaces from "../../../elements/Spaces"; +import {STD_BENIGN_COLOR, STD_PATHOGENIC_COLOR, STD_UNCERTAIN_COLOR} from "./PredConstants"; +import {pubmedRef} from "../../common/Common"; + +export const EVE_SCORE_ATTR: {[key: string]: PredAttr} = { + BENIGN: { color: 'blue', stdColor: STD_BENIGN_COLOR, title: 'benign' }, + UNCERTAIN: { color: 'lightgrey', stdColor: STD_UNCERTAIN_COLOR, title: 'uncertain' }, + PATHOGENIC: { color: 'red', stdColor: STD_PATHOGENIC_COLOR, title: 'pathogenic' } +} + +export const EvePred = (props: { eve?: EVEScore, stdColor: boolean }) => { + if (props.eve === undefined || props.eve === null) { + return <> + } + return
      +
      EVE {pubmedRef(PUBMED_ID.EVE)}
      +
      {props.eve.score}
      +
      +
      +} + +function EvePredIcon(props: { eve?: EVEScore, stdColor: boolean }) { + if (props.eve) { + let cls = props.eve.eveClass as keyof PredAttr + return <> + + {EVE_SCORE_ATTR[cls].title} + + } + return <> +} \ No newline at end of file diff --git a/src/ui/components/function/prediction/FoldxPred.tsx b/src/ui/components/function/prediction/FoldxPred.tsx new file mode 100644 index 00000000..b0bfce09 --- /dev/null +++ b/src/ui/components/function/prediction/FoldxPred.tsx @@ -0,0 +1,33 @@ +import {Foldx} from "../../../../types/FunctionalResponse"; +import Spaces from "../../../elements/Spaces"; +import {PUBMED_ID} from "./Prediction"; +import {Info, pubmedRef} from "../../common/Common"; + +export const FoldxPred = (props: { foldxs: Array }) => { + if (props.foldxs === null || props.foldxs.length === 0) { + return <> + } + return
      +
      +
      Stability change ΔΔG{pubmedRef(PUBMED_ID.FOLDX)}
      +
      {props.foldxs[0].foldxDdg} + +
      +
      +
      +
      +} + +function FoldxPredIcon(props: { foldx?: Foldx }) { + if (props.foldx) { + const color = props.foldx.foldxDdg >= 2 ? 'red' : 'blue' + const text = props.foldx.foldxDdg >= 2 ? 'likely to be destabilising' : 'unlikely to be destabilising' + return <> + + {text} + + + } + return <> +} \ No newline at end of file diff --git a/src/ui/components/function/prediction/PredConstants.ts b/src/ui/components/function/prediction/PredConstants.ts new file mode 100644 index 00000000..3c3fc63f --- /dev/null +++ b/src/ui/components/function/prediction/PredConstants.ts @@ -0,0 +1,7 @@ +import tinygradient from "tinygradient"; + +export const STD_BENIGN_COLOR: string = 'blue' +export const STD_UNCERTAIN_COLOR: string = 'lightgray' +export const STD_PATHOGENIC_COLOR: string = 'red' + +export const STD_COLOR_GRADIENT = tinygradient(STD_BENIGN_COLOR, STD_UNCERTAIN_COLOR, STD_PATHOGENIC_COLOR); diff --git a/src/ui/components/function/prediction/Prediction.tsx b/src/ui/components/function/prediction/Prediction.tsx new file mode 100644 index 00000000..bcd75320 --- /dev/null +++ b/src/ui/components/function/prediction/Prediction.tsx @@ -0,0 +1,37 @@ +import {MappingRecord} from "../../../../utills/Convertor"; +import {ConservPred} from "./ConservPred"; +import {AlphaMissensePred} from "./AlphaMissensePred"; +import {EvePred} from "./EvePred"; +import {EsmPred} from "./EsmPred"; +import {FoldxPred} from "./FoldxPred"; +import {Foldx} from "../../../../types/FunctionalResponse"; +import {useContext} from "react"; +import {StdColorContext} from "../../../App"; + +export type PredAttr = { + title: string, + color: string, + stdColor: string, + info?: string +} +export const PUBMED_ID = { + CONSERV: 11093265, + AM: 37733863, + EVE: 34707284, + ESM: 33876751, + FOLDX: 15980494, + INTERFACES: 36690744 +} + +export const Prediction = (props: { record: MappingRecord, foldxs: Array }) => { + const stdColor = useContext(StdColorContext); + return <>
      + + Structure predictions
      + + Pathogenicity predictions
      + + + + +} \ No newline at end of file diff --git a/src/ui/components/search/CaddHelper.ts b/src/ui/components/search/CaddHelper.ts deleted file mode 100644 index 28ae3028..00000000 --- a/src/ui/components/search/CaddHelper.ts +++ /dev/null @@ -1,36 +0,0 @@ -export function getColor(CADD: number) { - if (CADD < 15) { - return 'DarkGreen'; - } else if (CADD >= 15 && CADD < 20) { - return 'DarkSeaGreen'; - } else if (CADD >= 20 && CADD < 25) { - return 'Gold'; - } else if (CADD >= 25 && CADD < 30) { - return 'DarkOrange'; - } else if (CADD >= 30) { - return 'FireBrick'; - } -} - -export function getTitle(cadd: string | undefined) { - const CADD = parseFloat(cadd!) - if (CADD < 15) { - return '< 15.0 - Likely benign';; - } else if (CADD >= 15 && CADD < 20) { - return '15.0 to 19.9 - Potentially deleterious'; - } else if (CADD >= 20 && CADD < 25) { - return '20.0 to 24.9 - Quite likely deleterious'; - } else if (CADD >= 25 && CADD < 30) { - return '25.0 to 29.9 - Probably deleterious'; - } else if (CADD >= 30) { - return '> 29.9 - Highly likely deleterious'; - } -} - -export function getCaddCss(CADD: string | undefined) { - if (CADD === undefined || CADD === '-') { - return ''; - } else { - return `label warning cadd-score cadd-score--${getColor(parseFloat(CADD))}` - } -} \ No newline at end of file diff --git a/src/ui/components/search/CaddLegendColors.tsx b/src/ui/components/search/CaddLegendColors.tsx deleted file mode 100644 index 45124be3..00000000 --- a/src/ui/components/search/CaddLegendColors.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { EmptyElement } from "../../../constants/ConstElement"; -import { getColor, getTitle } from "./CaddHelper"; - -function CaddLegendColors() { - return ( -
      - CADD phred-like score colour -
      -
      -
      - - - - - -
      -
      - ); -} - -function CaddColourLi(props: { cadd: number }) { - const texts = getTitle(props.cadd.toString())?.split("-") - if (!texts || texts.length < 2) - return EmptyElement - return <> -
      -
      -
      {texts[0]}
      -
      {texts[1]}
      -
      - -} -export default CaddLegendColors; diff --git a/src/ui/components/search/CaddScorePred.ts b/src/ui/components/search/CaddScorePred.ts new file mode 100644 index 00000000..c5f8b4bb --- /dev/null +++ b/src/ui/components/search/CaddScorePred.ts @@ -0,0 +1,28 @@ +import {PredAttr} from "../function/prediction/Prediction"; +import {STD_COLOR_GRADIENT} from "../function/prediction/PredConstants"; + +export const CADD_SCORE_ATTR: PredAttr[] = [ + {color: 'DarkGreen', stdColor: STD_COLOR_GRADIENT.rgbAt(0).toHexString(), title: '<15.0 Likely benign' }, + {color: 'DarkSeaGreen', stdColor: STD_COLOR_GRADIENT.rgbAt(0.5).toHexString(), title: '15.0-19.9 Potentially deleterious' }, + {color: 'Gold', stdColor: STD_COLOR_GRADIENT.rgbAt(0.65).toHexString(), title: '20.0-24.9 Quite likely deleterious' }, + {color: 'DarkOrange', stdColor: STD_COLOR_GRADIENT.rgbAt(0.8).toHexString(), title: '25.0-29.9 Probably deleterious' }, + {color: 'FireBrick', stdColor: STD_COLOR_GRADIENT.rgbAt(1).toHexString(), title: '>29.9 Highly likely deleterious' } +] + +export function caddScoreAttr(cadd?: string) { + if (cadd === undefined || cadd === '-') { + return null; + } + let score = parseFloat(cadd) + let idx: number = 0 + if (score >= 15 && score < 20) { + idx = 1 + } else if (score >= 20 && score < 25) { + idx = 2 + } else if (score >= 25 && score < 30) { + idx = 3 + } else if (score >= 30) { + idx = 4 + } + return CADD_SCORE_ATTR[idx]; +} \ No newline at end of file diff --git a/src/ui/components/search/EveScore.tsx b/src/ui/components/search/EveScore.tsx deleted file mode 100644 index 6f87cc37..00000000 --- a/src/ui/components/search/EveScore.tsx +++ /dev/null @@ -1,22 +0,0 @@ -export function getEveClassText(eveClass?: number) { - switch(eveClass) { - case 1: return "Benign"; - case 2: return "Pathogenic"; - case 3: return "Uncertain"; - default: return "N/A"; - } -} - -export interface EveIconProps { - eveScore?: string - eveClass?: number -} - -export function EveIcon(props: EveIconProps) { - switch(props.eveClass) { - case 1: return - case 2: return - case 3: return - default: return <> - } -} \ No newline at end of file diff --git a/src/ui/components/search/EveScoreColors.tsx b/src/ui/components/search/EveScoreColors.tsx deleted file mode 100644 index e7644823..00000000 --- a/src/ui/components/search/EveScoreColors.tsx +++ /dev/null @@ -1,32 +0,0 @@ - -function EveScoreColors() { - return ( -
      - EVE score colour -
      -
      -
      -
      - - - -
      Benign
      -
      -
      - - - -
      Pathogenic
      -
      -
      - - - -
      Uncertain
      -
      -
      -
      - ); -} - -export default EveScoreColors; diff --git a/src/ui/components/search/PrimaryRow.css b/src/ui/components/search/PrimaryRow.css new file mode 100644 index 00000000..bd8b3af4 --- /dev/null +++ b/src/ui/components/search/PrimaryRow.css @@ -0,0 +1,13 @@ +.score-box { + width: 2rem; + text-align: center; + display: inline-block; + padding: 0.1rem 0.2rem; + margin-right: 1rem; + margin-bottom: 0.3rem; + border: none; + border-radius: 0.75rem; + font-size: 0.8rem; + font-weight: 700; + color: white; +} \ No newline at end of file diff --git a/src/ui/components/search/PrimaryRow.tsx b/src/ui/components/search/PrimaryRow.tsx index e9b8d56b..f63f7074 100644 --- a/src/ui/components/search/PrimaryRow.tsx +++ b/src/ui/components/search/PrimaryRow.tsx @@ -1,7 +1,9 @@ -import { Fragment, lazy, Suspense } from "react"; +import './PrimaryRow.css'; +import {Fragment, lazy, Suspense} from "react"; import { StringVoidFun } from "../../../constants/CommonTypes"; import { - CADD_INFO_URL, CLINVAR_RCV_URL, CLINVAR_VCV_URL, COSMIC_URL, + CADD_INFO_URL, AM_INFO_URL, + CLINVAR_RCV_URL, CLINVAR_VCV_URL, COSMIC_URL, DBSNP_URL, ENSEMBL_CHRM_URL, ENSEMBL_GENE_URL, @@ -12,7 +14,8 @@ import { ALLELE, CONSEQUENCES } from "../../../constants/SearchResultTable"; import { MappingRecord } from "../../../utills/Convertor"; import Spaces from "../../elements/Spaces"; import Tool from "../../elements/Tool"; -import { getCaddCss, getTitle } from "./CaddHelper"; +import {caddScoreAttr} from "./CaddScorePred"; +import {amScoreAttr} from "../function/prediction/AlphaMissensePred"; import { getProteinName } from "./ResultTable"; import ProteinIcon from '../../../images/proteins.svg'; import StructureIcon from '../../../images/structures-3d.svg'; @@ -22,7 +25,6 @@ import { ReactComponent as ChevronDownIcon } from "../../../images/chevron-down. import { ReactComponent as ChevronUpIcon } from "../../../images/chevron-up.svg" import { EmptyElement } from "../../../constants/ConstElement"; import { aaChangeTip, CanonicalIcon } from "./AlternateIsoFormRow"; -import {EveIcon, getEveClassText} from "./EveScore"; import {INPUT_GEN, INPUT_PRO, INPUT_ID, INPUT_CDNA} from "../../../types/MappingResponse"; const StructuralDetail = lazy(() => import(/* webpackChunkName: "StructuralDetail" */ "../structure/StructuralDetail")); @@ -30,9 +32,11 @@ const PopulationDetail = lazy(() => import(/* webpackChunkName: "PopulationDetai const FunctionalDetail = lazy(() => import(/* webpackChunkName: "FunctionalDetail" */ "../function/FunctionalDetail")); const getPrimaryRow = (record: MappingRecord, toggleOpenGroup: string, isoFormGroupExpanded: string, toggleIsoFormGroup: StringVoidFun, - annotationExpanded: string, toggleAnnotation: StringVoidFun, hasAltIsoForm: boolean, currStyle: object) => { - let caddCss = getCaddCss(record.CADD); - let caddTitle = getTitle(record.CADD); + annotationExpanded: string, toggleAnnotation: StringVoidFun, hasAltIsoForm: boolean, currStyle: object, stdColor: boolean) => { + + const caddAttr = caddScoreAttr(record.cadd) + const amAttr = amScoreAttr(record.amScore?.amClass) + let strand = record.strand ? '(-)' : '(+)'; if (!record.codon) { strand = ''; @@ -105,9 +109,9 @@ const getPrimaryRow = (record: MappingRecord, toggleOpenGroup: string, isoFormGr - + - 9 ? 0 : 2} />{isNaN(parseFloat(record.CADD!)) ? "" : parseFloat(record.CADD!).toFixed(1)} + 9 ? 0 : 2} />{isNaN(parseFloat(record.cadd!)) ? "" : parseFloat(record.cadd!).toFixed(1)} @@ -138,12 +142,16 @@ const getPrimaryRow = (record: MappingRecord, toggleOpenGroup: string, isoFormGr {record.aaPos} {record.aaChange} {record.consequences} - - - - + + + + {record.amScore?.amPathogenicity.toString().substring(0,4)} + + + +
      - {!record.canonical && <>

      } + {!record.canonical && <>

      } {getSignificancesButton(functionalKey, 'FUN', record, annotationExpanded, toggleAnnotation)} {getSignificancesButton(populationKey, 'POP', record, annotationExpanded, toggleAnnotation)} {getSignificancesButton(structuralKey, 'STR', record, annotationExpanded, toggleAnnotation)} @@ -163,8 +171,7 @@ const getPrimaryRow = (record: MappingRecord, toggleOpenGroup: string, isoFormGr } {functionalKey === annotationExpanded && }> - + } diff --git a/src/ui/components/search/ResultTable.tsx b/src/ui/components/search/ResultTable.tsx index 692ae15e..278e918f 100644 --- a/src/ui/components/search/ResultTable.tsx +++ b/src/ui/components/search/ResultTable.tsx @@ -1,4 +1,4 @@ -import { useState } from "react" +import {useContext, useState} from "react" import { StringVoidFun } from "../../../constants/CommonTypes"; import { MappingRecord } from "../../../utills/Convertor"; import AlternateIsoFormRow from "./AlternateIsoFormRow"; @@ -6,6 +6,7 @@ import { GENOMIC_COLS, PROTEIN_COLS } from "../../../constants/SearchResultTable import Tool from "../../elements/Tool"; import getPrimaryRow from "./PrimaryRow"; import MsgRow from "./MsgRow"; +import {StdColorContext} from "../../App"; interface ResultTableProps { mappings: Array>> @@ -20,6 +21,7 @@ export function getProteinName(record: MappingRecord) { } function ResultTable(props: ResultTableProps) { + const stdColor = useContext(StdColorContext); const [isoFormGroupExpanded, setIsoFormGroupExpanded] = useState('') const [annotationExpanded, setAnnotationExpanded] = useState('') @@ -31,7 +33,7 @@ function ResultTable(props: ResultTableProps) { setAnnotationExpanded(annotationExpanded === key ? '' : key); } - const tableRows = getTableRows(props.mappings, isoFormGroupExpanded, toggleIsoFormGroup, annotationExpanded, toggleAnnotation); + const tableRows = getTableRows(props.mappings, isoFormGroupExpanded, toggleIsoFormGroup, annotationExpanded, toggleAnnotation, stdColor); return @@ -48,7 +50,7 @@ function ResultTable(props: ResultTableProps) { Alt.GeneCodon (strand) - CADD + CADDIsoform @@ -56,7 +58,7 @@ function ResultTable(props: ResultTableProps) { AA pos.AA changeConsequence(s) - EVE + AlphaMiss. pred. @@ -67,7 +69,7 @@ function ResultTable(props: ResultTableProps) { } const getTableRows = (mappings: MappingRecord[][][], isoFormGroupExpanded: string, toggleIsoFormGroup: StringVoidFun, - annotationExpanded: string, toggleAnnotation: StringVoidFun) => { + annotationExpanded: string, toggleAnnotation: StringVoidFun, stdColor: boolean) => { const tableRows: Array = []; const rowStyle = { a: {backgroundColor: "#F4F3F3" }, b: {backgroundColor: "#FFFFFF" }} let prevInput = "" @@ -91,7 +93,7 @@ const getTableRows = (mappings: MappingRecord[][][], isoFormGroupExpanded: strin } else tableRows.push(getPrimaryRow(isoform, currentGroup, isoFormGroupExpanded, toggleIsoFormGroup, annotationExpanded, - toggleAnnotation, matchingIsoForms.length > 1, currStyle)) + toggleAnnotation, matchingIsoForms.length > 1, currStyle, stdColor)) else if (currentGroup === isoFormGroupExpanded) tableRows.push() } diff --git a/src/ui/layout/DefaultPageLayout.tsx b/src/ui/layout/DefaultPageLayout.tsx index 6f523f23..784910dd 100644 --- a/src/ui/layout/DefaultPageLayout.tsx +++ b/src/ui/layout/DefaultPageLayout.tsx @@ -16,7 +16,7 @@ interface DefaultPageLayoutProps { searchResults?: MappingRecord[][][] } -const bannerText = null +const bannerText = "AlphaMissense prediction has replaced EVE score in the main table. You can now find EVE score under Predictions in the Functional Information section." function DefaultPageLayout(props: DefaultPageLayoutProps) { const [showBanner, setShowBanner ] = useState(bannerText == null ? false : true); diff --git a/src/ui/components/search/ResultTableButtonsLegend.tsx b/src/ui/modal/AnnotationLegend.tsx similarity index 80% rename from src/ui/components/search/ResultTableButtonsLegend.tsx rename to src/ui/modal/AnnotationLegend.tsx index 48492b9c..c582e04c 100644 --- a/src/ui/components/search/ResultTableButtonsLegend.tsx +++ b/src/ui/modal/AnnotationLegend.tsx @@ -1,8 +1,8 @@ -import ProteinIcon from '../../../images/proteins.svg' -import StructureIcon from '../../../images/structures-3d.svg' -import PopulationIcon from '../../../images/human.svg' +import ProteinIcon from '../../images/proteins.svg' +import StructureIcon from '../../images/structures-3d.svg' +import PopulationIcon from '../../images/human.svg' -function ResultTableButtonsLegend() { +function AnnotationLegend() { return (
      Annotations @@ -41,4 +41,4 @@ function ResultTableButtonsLegend() { ) } -export default ResultTableButtonsLegend +export default AnnotationLegend diff --git a/src/ui/modal/LegendModal.tsx b/src/ui/modal/LegendModal.tsx index c373a078..d6ab8254 100644 --- a/src/ui/modal/LegendModal.tsx +++ b/src/ui/modal/LegendModal.tsx @@ -1,12 +1,17 @@ -import { useState, useCallback, useRef } from 'react' +import {useState, useCallback, useRef, useContext} from 'react' +import { v1 as uuidv1 } from 'uuid'; import Button from '../elements/form/Button' import Modal from './Modal' import useOnClickOutside from '../../hooks/useOnClickOutside' -import ResultTableButtonsLegend from '../components/search/ResultTableButtonsLegend' -import EveScoreColors from '../components/search/EveScoreColors' -import CaddLegendColors from '../components/search/CaddLegendColors' +import AnnotationLegend from './AnnotationLegend' +import {CADD_SCORE_ATTR} from "../components/search/CaddScorePred"; +import {PredAttr} from "../components/function/prediction/Prediction"; +import {AM_SCORE_ATTR} from "../components/function/prediction/AlphaMissensePred"; +import {EVE_SCORE_ATTR} from "../components/function/prediction/EvePred"; +import {StdColorContext} from "../App"; -function LegendModal() { +function LegendModal(props: {toggleStdColor: () => void}) { + const stdColor = useContext(StdColorContext); const [showModel, setShowModel] = useState(false) const downloadModelDiv = useRef(null) @@ -15,6 +20,7 @@ function LegendModal() { useCallback(() => setShowModel(false), []), ) + return (
      - + +
      +
      +
      +
      - +
      +
      - +
      +
      + +
      ) } + +interface CommonLegendProps { + stdColor: boolean +} + +function CaddLegend(props: CommonLegendProps) { + return ( +
      + CADD phred-like score +
      +
      + { + Object.values(CADD_SCORE_ATTR).map((sc: PredAttr) => { + return
      + + + +
      {sc.title}
      +
      ; + }) + } +
      +
      + ); +} + +function ConservLegend() { + return ( +
      + Residue conservation +
      +
      +
      +
      + +
      +
      Low High
      +
      +
      +
      +
      + ); +} + +function EsmLegend(props: CommonLegendProps) { + return ( +
      + ESM1b LLR score +
      +
      +
      +
      + +
      +
      0 -5 -10 -15 -20 -25
      +
      +
      +
      +
      + ); +} + +function AlphaMissenseLegend(props: CommonLegendProps) { + return
      + AlphaMissense score +
      +
      + { + Object.values(AM_SCORE_ATTR).map((sc: PredAttr) => { + return
      + + + +
      {sc.title}
      +
      ; + }) + } +
      +
      +
      ; +} + +function EveLegend(props: CommonLegendProps) { + return ( +
      + EVE score +
      +
      + { + Object.values(EVE_SCORE_ATTR).map((sc: PredAttr) => { + return
      + + + +
      {sc.title}
      +
      ; + }) + } +
      +
      +
      + ); +} + export default LegendModal diff --git a/src/ui/pages/query/QueryPage.tsx b/src/ui/pages/query/QueryPage.tsx index bbc7370e..1774f294 100644 --- a/src/ui/pages/query/QueryPage.tsx +++ b/src/ui/pages/query/QueryPage.tsx @@ -157,7 +157,7 @@ function getInputsFromUrl(location: any): any { return [] } -const QueryPageContent = () => { +const QueryPageContent = (props: {toggleStdColor: () => void}) => { const location = useLocation() const [loaded, setLoaded] = useState(false) const [err, setErr] = useState(false) @@ -190,7 +190,7 @@ const QueryPageContent = () => {
      - +
      @@ -203,8 +203,8 @@ const QueryPageContent = () => { return <>{loaded ? result : (err ? : )} } -function QueryPage() { - return } /> +function QueryPage(props: {toggleStdColor: () => void}) { + return } /> } export default QueryPage diff --git a/src/ui/pages/search/SearchResultPage.tsx b/src/ui/pages/search/SearchResultPage.tsx index 6505eac1..77806188 100644 --- a/src/ui/pages/search/SearchResultPage.tsx +++ b/src/ui/pages/search/SearchResultPage.tsx @@ -16,6 +16,7 @@ interface SearchResultPageProps { fetchNextPage: NextPageFun rows: MappingRecord[][][] loading: boolean + toggleStdColor: () => void } function SearchResultsPageContent(props: SearchResultPageProps) { @@ -34,7 +35,7 @@ function SearchResultsPageContent(props: SearchResultPageProps) {
      - +
      diff --git a/src/utills/Convertor.ts b/src/utills/Convertor.ts index ce217f11..9b830bea 100644 --- a/src/utills/Convertor.ts +++ b/src/utills/Convertor.ts @@ -6,7 +6,7 @@ import { INPUT_CDNA, UserInput, GenomicInput, - Message, MappingResponse + Message, MappingResponse, EVEScore, ConservScore, ESMScore, AMScore } from "../types/MappingResponse"; export interface MappingRecord { @@ -23,7 +23,7 @@ export interface MappingRecord { geneName?: string codon?: string strand?: boolean - CADD?: string + cadd?: string // PROTEIN column properties canonical?: boolean // display as can (true) or iso (false) isoform?: string @@ -35,8 +35,10 @@ export interface MappingRecord { variantAA?: string cdsPosition?: number // not displayed or used anywhere consequences?: string - eveScore?: string - eveClass?: number + conservScore?: ConservScore + amScore?: AMScore + eveScore?: EVEScore + esmScore?: ESMScore // ANNOTATIONS column referenceFunctionUri?: string populationObservationsUri?: string @@ -169,8 +171,8 @@ function convertGenInputMappings(originalInput: UserInput, genInput: GenomicInpu record.geneName = gene.geneName; record.codon = isoform.refCodon + '/' + isoform.variantCodon; record.strand = gene.reverseStrand; - if (gene.caddScore === null) record.CADD = '-'; - else record.CADD = gene.caddScore.toString(); + if (gene.caddScore === null) record.cadd = '-'; + else record.cadd = gene.caddScore.toString(); } // PROTEIN record.canonical = isoform.canonical; @@ -183,12 +185,10 @@ function convertGenInputMappings(originalInput: UserInput, genInput: GenomicInpu record.variantAA = isoform.variantAA; record.cdsPosition = isoform.cdsPosition; record.consequences = isoform.consequences; - if (isoform.eveScore !== undefined && isoform.eveScore !== null) { - record.eveScore = isoform.eveScore.toString(); - record.eveClass = isoform.eveClass; - } else { - record.eveScore = '-'; - } + record.conservScore = isoform.conservScore; + record.amScore = isoform.amScore; + record.eveScore = isoform.eveScore; + record.esmScore = isoform.esmScore; // ANNOTATIONS record.referenceFunctionUri = isoform.referenceFunctionUri; record.populationObservationsUri = isoform.populationObservationsUri;
      Click for details