Skip to content

Commit

Permalink
feat: adding StrucvarToolsCard (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe authored Jan 30, 2024
1 parent 9c84f26 commit ae5ae68
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/components/StrucvarToolsCard/StrucvarToolsCard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, it } from 'vitest'

import { type Strucvar } from '../../lib/genomicVars'
import { setupMountedComponents } from '../../lib/testUtils'
import StrucvarToolsCard from './StrucvarToolsCard.vue'

/** Example Strucvar */
const strucvarInfo: Strucvar = {
genomeBuild: 'grch37',
svType: 'DEL',
chrom: '17',
start: 41176312,
stop: 41277500,
userRepr: 'DEL-grch37-17-41176312-41277500'
}

describe.concurrent('StrucvarToolsCard.vue', async () => {
it('renders content', async () => {
// arrange:
const { wrapper } = await setupMountedComponents(
{ component: StrucvarToolsCard },
{
props: {
strucvar: structuredClone(strucvarInfo),
genomeBuild: 'grch37'
}
}
)

// act: nothing, only test rendering

// assert:
expect(wrapper.text()).toContain('Variant Tools')
const vButtons = wrapper.findAllComponents({ name: 'VBtn' })
expect(vButtons.length).toBe(7)
})
})
178 changes: 178 additions & 0 deletions src/components/StrucvarToolsCard/StrucvarToolsCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { GenomeBuild } from '../../lib/genomeBuilds'
import { type Strucvar } from '../../lib/genomicVars'
import DocsLink from '../DocsLink/DocsLink.vue'
/** Type for this component's props. */
export interface Props {
genomeBuild?: GenomeBuild
// eslint-disable-next-line vue/require-default-prop
strucvar?: Strucvar
}
/** The component's props, fallback for genome build is GRCh37. */
const props = withDefaults(defineProps<Props>(), {
genomeBuild: 'grch37'
})
const ucscLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const db = props.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
return (
`https://genome-euro.ucsc.edu/cgi-bin/hgTracks?db=${db}&position=` +
`${props.strucvar.chrom}:${props.strucvar.start}-` +
`${props.strucvar.stop}`
)
})
const ensemblLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const loc = `${props.strucvar.chrom}:${props.strucvar.start}-${props.strucvar.stop}`
if (props.genomeBuild === 'grch37') {
return `https://grch37.ensembl.org/Homo_sapiens/Location/View?r=${loc}`
} else if (props.genomeBuild === 'grch38') {
return `https://ensembl.org/Homo_sapiens/Location/View?r=${loc}`
}
return '#'
})
const dgvLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const db = props.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
return (
`http://dgv.tcag.ca/gb2/gbrowse/dgv2_${db}/?name=${props.strucvar.chrom}:` +
`${props.strucvar.start}-${props.strucvar.stop};search=Search`
)
})
const gnomadLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const dataset = props.genomeBuild === 'grch37' ? 'gnomad_r2_1' : 'gnomad_r3'
return (
`https://gnomad.broadinstitute.org/region/${props.strucvar.chrom}:` +
`${props.strucvar.start}-${props.strucvar.stop}?dataset=${dataset}`
)
})
const varsomeLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const urlRelease = props.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
const chrom = props.strucvar.chrom.startsWith('chr')
? props.strucvar.chrom
: `chr${props.strucvar.chrom}`
return `https://varsome.com/cnv/${urlRelease}/${chrom}:${props.strucvar.start}:${props.strucvar.stop}:${props.strucvar.svType}`
})
const franklinLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const { chrom, start, stop, svType } = props.strucvar
const urlRelease = props.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
return `https://franklin.genoox.com/clinical-db/variant/sv/chr${chrom}-${start}-${stop}-${svType}-${urlRelease}`
})
const jumpToLocus = async () => {
const chrPrefixed = props.strucvar?.chrom.startsWith('chr')
? props.strucvar?.chrom
: `chr${props.strucvar?.chrom}`
// NB: we allow the call to fetch here as it goes to local IGV.
await fetch(
`http://127.0.0.1:60151/goto?locus=${chrPrefixed}:${props.strucvar?.start}-${props.strucvar?.stop}`
).catch((e) => {
const msg = "Couldn't connect to IGV. Please make sure IGV is running and try again."
alert(msg)
console.error(msg, e)
})
}
interface Linkout {
href: string
label: string
}
const genomeBrowsers = computed<Linkout[]>(() => {
return [
{ label: 'ENSEMBL', href: ensemblLinkout.value },
{ label: 'UCSC', href: ucscLinkout.value }
]
})
const resources = computed<Linkout[]>(() => {
return [
{ label: 'DGV', href: dgvLinkout.value },
{ label: 'gnomAD', href: gnomadLinkout.value },
{ label: 'varsome', href: varsomeLinkout.value },
{ label: 'genoox Franklin', href: franklinLinkout.value }
]
})
</script>

<template>
<v-card class="mt-3">
<v-card-title class="pb-0 pr-2">
Variant Tools
<DocsLink anchor="clinvar-information" />
</v-card-title>

<v-card-subtitle class="text-overline"> Genome Browsers </v-card-subtitle>
<v-card-text>
<v-btn
v-for="linkout in genomeBrowsers"
:key="linkout.label"
:href="linkout.href"
target="_blank"
prepend-icon="mdi-launch"
variant="tonal"
rounded="sm"
class="mr-2"
>
{{ linkout.label }}
</v-btn>
</v-card-text>

<v-card-subtitle class="text-overline"> Other Resources </v-card-subtitle>
<v-card-text>
<v-btn
v-for="linkout in resources"
:key="linkout.label"
:href="linkout.href"
target="_blank"
prepend-icon="mdi-launch"
variant="tonal"
rounded="sm"
class="mr-2"
>
{{ linkout.label }}
</v-btn>
</v-card-text>

<v-card-actions>
<v-btn prepend-icon="mdi-launch" @click.prevent="jumpToLocus()"> Jump in Local IGV </v-btn>
</v-card-actions>
</v-card>
</template>

<style scoped>
.external-resource-item {
float: left;
margin-right: 10px;
}
.external-resource-item:last-child {
float: none;
margin-right: 0;
}
</style>

0 comments on commit ae5ae68

Please sign in to comment.