Skip to content

Commit

Permalink
feat: Link-outs in SV detail page (#143) (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
gromdimon authored Oct 18, 2023
1 parent bc062d8 commit 17171a7
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
146 changes: 146 additions & 0 deletions frontend/src/components/SvDetails/SvTools.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<script setup lang="ts">
import { computed } from 'vue'
import { type SvRecord } from '@/stores/svInfo'
export interface Props {
svRecord: SvRecord | null
}
const props = withDefaults(defineProps<Props>(), {
svRecord: null
})
const ucscLinkout = computed((): string => {
if (!props.svRecord) {
return '#'
}
const db = props.svRecord.release === 'grch37' ? 'hg19' : 'hg38'
return (
`https://genome-euro.ucsc.edu/cgi-bin/hgTracks?db=${db}&position=` +
`${props.svRecord.chromosome}:${props.svRecord.start}-` +
`${props.svRecord.end}`
)
})
const ensemblLinkout = computed((): string => {
if (!props.svRecord) {
return '#'
}
const loc = `${props.svRecord.chromosome}:${props.svRecord.start}-${props.svRecord.end}`
if (props.svRecord.release === 'grch37') {
return `https://grch37.ensembl.org/Homo_sapiens/Location/View?r=${loc}`
} else if (props.svRecord.release === 'grch38') {
return `https://ensembl.org/Homo_sapiens/Location/View?r=${loc}`
}
return '#'
})
const dgvLinkout = computed((): string => {
if (!props.svRecord) {
return '#'
}
const db = props.svRecord.release === 'grch37' ? 'hg19' : 'hg38'
return (
`http://dgv.tcag.ca/gb2/gbrowse/dgv2_${db}/?name=${props.svRecord.chromosome}:` +
`${props.svRecord.start}-${props.svRecord.end};search=Search`
)
})
const gnomadLinkout = computed((): string => {
if (!props.svRecord) {
return '#'
}
const dataset = props.svRecord.release === 'grch37' ? 'gnomad_r2_1' : 'gnomad_r3'
return (
`https://gnomad.broadinstitute.org/region/${props.svRecord.chromosome}:` +
`${props.svRecord.start}-${props.svRecord.end}?dataset=${dataset}`
)
})
const varsomeLinkout = computed((): string => {
if (!props.svRecord) {
return '#'
}
const urlRelease = props.svRecord.release === 'grch37' ? 'hg19' : 'hg38'
const chrom = props.svRecord.chromosome.startsWith('chr')
? props.svRecord.chromosome
: `chr${props.svRecord.chromosome}`
return `https://varsome.com/cnv/${urlRelease}/${chrom}:${props.svRecord.start}:${props.svRecord.end}:${props.svRecord.svType}`
})
const jumpToLocus = async () => {
const chrPrefixed = props.svRecord?.chromosome.startsWith('chr')
? props.svRecord?.chromosome
: `chr${props.svRecord?.chromosome}`
await fetch(
`http://127.0.0.1:60151/goto?locus=${chrPrefixed}:${props.svRecord?.start}-${props.svRecord?.end}`
).catch((e) => {
const msg = "Couldn't connect to IGV. Please make sure IGV is running and try again."
alert(msg)
console.error(msg, e)
})
}
</script>

<template>
<div>
<h2>External Resources</h2>
<div class="external-resource-item">
<a :href="dgvLinkout" target="_blank">
<v-icon>mdi-launch</v-icon>
DGV
</a>
</div>
<div class="external-resource-item">
<a :href="ensemblLinkout" target="_blank">
<v-icon>mdi-launch</v-icon>
ENSEBML
</a>
</div>
<div class="external-resource-item">
<a :href="gnomadLinkout" target="_blank">
<v-icon>mdi-launch</v-icon>
gnomAD
</a>
</div>
<div class="external-resource-item">
<a :href="ucscLinkout" target="_blank">
<v-icon>mdi-launch</v-icon>
UCSC Genome Browser
</a>
</div>
<div class="external-resource-item">
<a :href="varsomeLinkout" target="_blank">
<v-icon>mdi-launch</v-icon>
varsome
</a>
</div>
</div>

<v-divider />

<div>
<h2>IGV</h2>
<div>
<div>
<a href="#" target="_blank" @click.prevent="jumpToLocus()">
<v-icon>mdi-launch</v-icon>
Jump to Location in IGV
</a>
</div>
</div>
</div>
</template>

<style scoped>
.external-resource-item {
float: left;
margin-right: 10px;
}
.external-resource-item:last-child {
float: none;
margin-right: 0;
}
</style>
62 changes: 62 additions & 0 deletions frontend/src/components/__tests__/SvDetails/SvTools.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, expect, it } from 'vitest'

import SvTools from '@/components/SvDetails/SvTools.vue'
import { setupMountedComponents } from '@/lib/test-utils'
import { type SvRecord } from '@/stores/svInfo'

const svRecord: SvRecord = {
svType: 'DEL',
chromosome: 'chr17',
start: '41176312',
end: '41277500',
release: 'grch37',
result: [
{
hgnc_id: 'HGNC:18315',
transcript_effects: [
'transcript_variant',
'exon_variant',
'splice_region_variant',
'intron_variant',
'upstream_variant',
'downstream_variant'
]
},
{
hgnc_id: 'HGNC:20691',
transcript_effects: ['upstream_variant']
},
{
hgnc_id: 'HGNC:1100',
transcript_effects: [
'transcript_variant',
'exon_variant',
'splice_region_variant',
'intron_variant',
'upstream_variant',
'downstream_variant'
]
},
{
hgnc_id: 'HGNC:16919',
transcript_effects: ['upstream_variant']
}
]
}

describe.concurrent('SvTools', async () => {
it('renders the SvTools content', async () => {
const { wrapper } = setupMountedComponents(
{ component: SvTools, template: false },
{
props: {
svRecord: svRecord
}
}
)

expect(wrapper.text()).toContain('External Resources')
const links = wrapper.findAll('a')
expect(links.length).toBe(6)
})
})
8 changes: 8 additions & 0 deletions frontend/src/views/SvDetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import HeaderDetailPage from '@/components/HeaderDetailPage.vue'
import AcmgRating from '@/components/SvDetails/AcmgRating.vue'
import SvDetailsClinvar from '@/components/SvDetails/SvDetailsClinvar.vue'
import SvDetailsGenes from '@/components/SvDetails/SvGenes.vue'
import SvTools from '@/components/SvDetails/SvTools.vue'
import { StoreState } from '@/stores/misc'
import { useSvInfoStore } from '@/stores/svInfo'
import { type SvRecord } from '@/stores/svInfo'
Expand Down Expand Up @@ -80,6 +81,7 @@ watch(
const SECTIONS = [
{ id: 'gene', title: 'Gene' },
{ id: 'clinvar', title: 'ClinVar' },
{ id: 'sv-tools', title: 'SV Tools' },
{ id: 'acmg', title: 'ACMG' },
{ id: 'genome-browser', title: 'Genome Browser' }
]
Expand Down Expand Up @@ -120,6 +122,12 @@ const genomeReleaseRef = ref(props.genomeRelease)
<SvDetailsClinvar />
</div>

<div id="sv-tools" class="sv-item">
<h2>SV Tools</h2>
<v-divider />
<SvTools :sv-record="svInfoStore.currentSvRecord" />
</div>

<div id="acmg" class="sv-item">
<h2>ACMG</h2>
<v-divider />
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/views/__tests__/SvDetailView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import HeaderDetailPage from '@/components/HeaderDetailPage.vue'
import AcmgRating from '@/components/SvDetails/AcmgRating.vue'
import SvDetailsClinvar from '@/components/SvDetails/SvDetailsClinvar.vue'
import SvGenes from '@/components/SvDetails/SvGenes.vue'
import SvTools from '@/components/SvDetails/SvTools.vue'
import { setupMountedComponents } from '@/lib/test-utils'
import { StoreState } from '@/stores/misc'
import { useSvInfoStore } from '@/stores/svInfo'
Expand Down Expand Up @@ -66,10 +67,12 @@ describe.concurrent('VariantDetailView', async () => {
const { wrapper } = makeWrapper()

const svGenes = wrapper.findComponent(SvGenes)
const svTools = wrapper.findComponent(SvTools)
const svDetailsClinvar = wrapper.findComponent(SvDetailsClinvar)
const acmgRating = wrapper.findComponent(AcmgRating)
const genomeBrowser = wrapper.findComponent(GenomeBrowser)
expect(svGenes.exists()).toBe(true)
expect(svTools.exists()).toBe(true)
expect(svDetailsClinvar.exists()).toBe(true)
expect(acmgRating.exists()).toBe(true)
expect(genomeBrowser.exists()).toBe(true)
Expand Down

0 comments on commit 17171a7

Please sign in to comment.