Skip to content

Commit

Permalink
Add mitochondrial regional constraint visualizations
Browse files Browse the repository at this point in the history
  • Loading branch information
phildarnowsky-broad committed Jun 4, 2024
1 parent 89aec0a commit c1ca289
Show file tree
Hide file tree
Showing 11 changed files with 378 additions and 54 deletions.
1 change: 1 addition & 0 deletions browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@gnomad/ui": "2.0.0",
"@hot-loader/react-dom": "^17.0.0",
"@visx/axis": "^3.0.0",
"@visx/group": "^3.0.0",
"core-js": "3.5.0",
"css-loader": "^6.7.3",
"d3-array": "^1.2.4",
Expand Down
45 changes: 38 additions & 7 deletions browser/src/ConstraintTrack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,33 @@ const TopPanel = styled.div`
margin-bottom: 5px;
`

const LegendWrapper = styled.div`
display: flex;
@media (max-width: 600px) {
flex-direction: column;
align-items: center;
}
`

export const RegionAttributeList = styled.dl`
margin: 0;
div {
margin-bottom: 0.25em;
}
dt {
display: inline;
font-weight: bold;
}
dd {
display: inline;
margin-left: 0.5em;
}
`

export interface GenericRegion {
start: number
stop: number
Expand All @@ -55,7 +82,7 @@ type Props<R extends GenericRegion> = {
constrainedRegions: RegionWithUnclamped<R>[]
infobuttonTopic: string
legend: ReactNode
tooltipComponent: any // TK any
tooltipComponent: React.ElementType
colorFn: (region: R) => string
valueFn: (region: R) => string
}
Expand Down Expand Up @@ -120,9 +147,12 @@ const ConstraintTrack = <R extends GenericRegion>({
>
{({ scalePosition, width }: TrackProps) => (
<>
<TopPanel>{legend}</TopPanel>
<TopPanel>
<LegendWrapper>{legend}</LegendWrapper>
</TopPanel>
<PlotWrapper>
<svg height={55} width={width}>
{!allRegions && <rect x={0} y={7.5} width={width} height={1} />}
{constrainedRegions.map((region: RegionWithUnclamped<R>) => {
const startX = scalePosition(region.start)
const stopX = scalePosition(region.stop)
Expand All @@ -139,7 +169,7 @@ const ConstraintTrack = <R extends GenericRegion>({
<g>
<rect
x={startX}
y={0}
y={1}
width={regionWidth}
height={15}
fill={colorFn(region)}
Expand All @@ -149,9 +179,9 @@ const ConstraintTrack = <R extends GenericRegion>({
</TooltipAnchor>
)
})}
<g transform="translate(0,20)">
{allRegions &&
allRegions.map((region: R, index: number) => {
{allRegions && (
<g transform="translate(0,20)">
{allRegions.map((region: R, index: number) => {
const startX = scalePosition(region.start)
const stopX = scalePosition(region.stop)
const regionWidth = stopX - startX
Expand Down Expand Up @@ -198,7 +228,8 @@ const ConstraintTrack = <R extends GenericRegion>({
</g>
)
})}
</g>
</g>
)}
</svg>
</PlotWrapper>
</>
Expand Down
46 changes: 46 additions & 0 deletions browser/src/GenePage/GenePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { Track } from '@gnomad/region-viewer'
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@gno... Remove this comment to see the full error message
import { TranscriptPlot } from '@gnomad/track-transcripts'
import { Badge, Button } from '@gnomad/ui'
import MitochondrialRegionConstraintTrack, {
MitochondrialConstraintRegion,
} from './MitochondrialRegionConstraintTrack'

import {
DatasetId,
Expand Down Expand Up @@ -72,6 +75,40 @@ import {
} from '../ChartStyles'
import { logButtonClick } from '../analytics'

type ProteinMitochondrialGeneConstraint = {
exp_lof: number
exp_mis: number
exp_syn: number

obs_lof: number
obs_mis: number
obs_syn: number

oe_lof: number
oe_lof_lower: number
oe_lof_upper: number

oe_mis: number
oe_mis_lower: number
oe_mis_upper: number

oe_syn: number
oe_syn_lower: number
oe_syn_upper: number
}

type RNAMitochondrialGeneConstraint = {
observed: number
expected: number
oe: number
oe_upper: number
oe_lower: number
}

type MitochondrialGeneConstraint =
| ProteinMitochondrialGeneConstraint
| RNAMitochondrialGeneConstraint

export type Strand = '+' | '-'

export type GeneMetadata = {
Expand Down Expand Up @@ -136,6 +173,8 @@ export type Gene = GeneMetadata & {
clinvar_variants: ClinvarVariant[]
homozygous_variant_cooccurrence_counts: HomozygousVariantCooccurrenceCountsPerSeverityAndAf
heterozygous_variant_cooccurrence_counts: HeterozygousVariantCooccurrenceCountsPerSeverityAndAf
mitochondrial_constraint: MitochondrialGeneConstraint | null
mitochondrial_missense_constraint_regions: MitochondrialConstraintRegion[] | null
}

const GeneName = styled.span`
Expand Down Expand Up @@ -528,6 +567,13 @@ const GenePage = ({ datasetId, gene, geneId }: Props) => {
</TrackWrapper>
)}

{gene.chrom.startsWith('M') && (
<MitochondrialRegionConstraintTrack
constraintRegions={gene.mitochondrial_missense_constraint_regions}
exons={gene.exons}
/>
)}

{hasCodingExons && gene.chrom !== 'M' && gene.pext && (
<TissueExpressionTrack
exons={cdsCompositeExons}
Expand Down
35 changes: 35 additions & 0 deletions browser/src/GenePage/GenePageContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,41 @@ query ${operationName}($geneId: String, $geneSymbol: String, $referenceGenome: R
hom_total
}
}
mitochondrial_constraint {
...on ProteinMitochondrialGeneConstraint {
exp_lof
exp_mis
exp_syn
obs_lof
obs_mis
obs_syn
oe_lof
oe_lof_lower
oe_lof_upper
oe_mis
oe_mis_lower
oe_mis_upper
oe_syn
oe_syn_lower
oe_syn_upper
}
...on RNAMitochondrialGeneConstraint {
observed
expected
oe
oe_upper
oe_lower
}
}
mitochondrial_missense_constraint_regions{
start
stop
oe
oe_upper
oe_lower
}
}
}
`
Expand Down
46 changes: 46 additions & 0 deletions browser/src/GenePage/MitochondrialRegionConstraintTrack.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react'
import renderer from 'react-test-renderer'
import { expect, test } from '@jest/globals'
import MitochondrialRegionConstraintTrack, {
MitochondrialConstraintRegion,
} from './MitochondrialRegionConstraintTrack'
import { Exon } from '../TranscriptPage/TranscriptPage'
// @ts-expect-error
import { RegionViewerContext } from '@gnomad/region-viewer'

const childProps = {
centerPanelWidth: 3,
isPositionDefined: true,
leftPanelWidth: 4,
regions: [],
rightPanelWidth: 5,
scalePosition: (i: number) => i,
}

const exons: Exon[] = [
{ feature_type: 'CDS', start: 123, stop: 234 },
{ feature_type: 'UTR', start: 235, stop: 999 },
{ feature_type: 'CDS', start: 1000, stop: 1999 },
]

test('track has no unexpected changes when gene has constraint', () => {
const constraintRegions: MitochondrialConstraintRegion[] = [
{ start: 555, stop: 666, oe: 0.45, oe_lower: 0.37, oe_upper: 0.47 },
{ start: 777, stop: 888, oe: 0.56, oe_lower: 0.52, oe_upper: 0.59 },
]
const tree = renderer.create(
<RegionViewerContext.Provider value={childProps}>
<MitochondrialRegionConstraintTrack constraintRegions={constraintRegions} exons={exons} />
</RegionViewerContext.Provider>
)
expect(tree).toMatchSnapshot()
})

test('track has no unexpected changes when no constraint for gene', () => {
const tree = renderer.create(
<RegionViewerContext.Provider value={childProps}>
<MitochondrialRegionConstraintTrack constraintRegions={null} exons={exons} />
</RegionViewerContext.Provider>
)
expect(tree).toMatchSnapshot()
})
74 changes: 74 additions & 0 deletions browser/src/GenePage/MitochondrialRegionConstraintTrack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react'
import { Exon } from '../TranscriptPage/TranscriptPage'
import ConstraintTrack, { regionsInExons, RegionAttributeList } from '../ConstraintTrack'

export type MitochondrialConstraintRegion = {
start: number
stop: number
oe: number
oe_upper: number
oe_lower: number
}

type Props = {
constraintRegions: MitochondrialConstraintRegion[] | null
exons: Exon[]
}

const constraintColor = '#fd8d3c'

const Legend = () => (
<>
<span>Constrained region</span>
<svg width={50} height={25}>
<rect x={10} y={3} width={30} height={10} stroke="#000" fill={constraintColor} />
</svg>
</>
)

type TooltipProps = {
region: MitochondrialConstraintRegion
}

const Tooltip = ({ region }: TooltipProps) => {
return (
<RegionAttributeList>
<div>
<dt>Coordinates:</dt>
<dd>{`M:${region.start}-${region.stop}`}</dd>
</div>
<div>
<dt>Missense observed/expected:</dt>
<dd>
{region.oe.toFixed(3)} ({region.oe_lower.toFixed(3)}-{region.oe_upper.toFixed(3)})
</dd>
</div>
</RegionAttributeList>
)
}

const formattedOE = (region: MitochondrialConstraintRegion) => region.oe.toFixed(3)

const MitochondrialConstraintRegionTrack = ({ constraintRegions, exons }: Props) => {
if (constraintRegions === null) {
return null
}

return (
<ConstraintTrack
trackTitle="Regional constraint"
infobuttonTopic="TK-mitochondrial-gene-constraint"
legend={<Legend />}
valueFn={formattedOE}
colorFn={() => constraintColor}
tooltipComponent={Tooltip}
allRegions={null}
constrainedRegions={regionsInExons(
constraintRegions,
exons.filter((exon) => exon.feature_type === 'CDS')
)}
/>
)
}

export default MitochondrialConstraintRegionTrack
Loading

0 comments on commit c1ca289

Please sign in to comment.