Skip to content

Commit

Permalink
feat: adding SeqvarClinvarCard (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe authored Jan 30, 2024
1 parent d5df980 commit 1cc2b6d
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/components/SeqvarClinvarCard/SeqvarClinvarCard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import fs from 'fs'
import path from 'path'
import { describe, expect, it } from 'vitest'

import { setupMountedComponents } from '../../lib/testUtils'
import { Record as ClinvarRecord } from '../../pbs/annonars/clinvar/minimal'
import SeqvarClinvarCard from './SeqvarClinvarCard.vue'

const clinvarRecordBrca1 = ClinvarRecord.fromJson(
JSON.parse(
fs.readFileSync(
path.resolve(__dirname, '../../api/annonars/fixture.variantInfo.BRCA1.json'),
'utf8'
)
).result.clinvar
)

describe.concurrent('SeqvarClinvarCard.vue', async () => {
it('renders the card', async () => {
// arrange:
const { wrapper } = await setupMountedComponents(
{ component: SeqvarClinvarCard },
{
props: {
clinvarRecord: clinvarRecordBrca1
}
}
)

// act: nothing, only test rendering

// assert:
expect(wrapper.text()).toContain('ClinVar')
expect(wrapper.text()).toContain('VCV000055407')
const stars = wrapper.findAll('.mdi-star')
expect(stars.length).toBe(15)
const starsOutline = wrapper.findAll('.mdi-star-outline')
expect(starsOutline.length).toBe(30)
})

it('renders the ClinVar info (not found)', async () => {
// arrange:
const { wrapper } = await setupMountedComponents(
{ component: SeqvarClinvarCard },
{
props: {
clinvarRecord: undefined
}
}
)

// act: nothing, only test rendering

// assert:
expect(wrapper.text()).toContain('No ClinVar information available.')
})
})
31 changes: 31 additions & 0 deletions src/components/SeqvarClinvarCard/SeqvarClinvarCard.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { JsonValue } from '@protobuf-ts/runtime'
import type { Meta, StoryObj } from '@storybook/vue3'

import variantInfoBrca1Json from '../../api/annonars/fixture.variantInfo.BRCA1.json'
import { Record as ClinvarRecord } from '../../pbs/annonars/clinvar/minimal'
import SeqvarClinvarCard from './SeqvarClinvarCard.vue'

// Here, fixture data is loaded via `import` from JSON file. Reading the file
// as in the tests fails with "process is not defined" error in the browser.

// @ts-ignore
const clinvarRecordBrca1 = ClinvarRecord.fromJson(variantInfoBrca1Json.result.clinvar as JsonValue)

const meta = {
title: 'Seqvar/SeqvarClinvarCard',
component: SeqvarClinvarCard,
tags: ['autodocs'],
argTypes: {
clinvarRecord: { control: { type: 'object' } }
},
args: { clinvarRecord: clinvarRecordBrca1 }
} satisfies Meta<typeof SeqvarClinvarCard>

export default meta
type Story = StoryObj<typeof meta>

export const BRCA1: Story = {
args: {
clinvarRecord: clinvarRecordBrca1
}
}
144 changes: 144 additions & 0 deletions src/components/SeqvarClinvarCard/SeqvarClinvarCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<script setup lang="ts">
import { ref } from 'vue'
import { Record as ClinvarRecord } from '../../pbs/annonars/clinvar/minimal'
import DocsLink from '../DocsLink/DocsLink.vue'
import { CLINICAL_SIGNIFICANCE_LABEL, REVIEW_STATUS_LABEL, REVIEW_STATUS_STARS } from './constants'
/** This component's clinvarRecord */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = defineProps<{
/** ClinVar record from annonars */
clinvarRecord?: ClinvarRecord
}>()
/** Convert VCV to number. */
const vcvToNumber = (vcv: string): number => {
return parseInt(vcv.substring(3))
}
/** Whether the card is expanded; component state. */
const expand = ref<boolean>(false)
</script>

<template>
<v-card v-if="clinvarRecord?.vcv">
<v-card-title class="pb-0 pr-2">
ClinVar
<DocsLink anchor="clinvar" />
</v-card-title>
<v-card-text>
<v-row no-gutters class="flex-nowrap">
<v-col cols="1" class="font-weight-black"> Significance </v-col>
<v-col cols="1" style="min-width: 100px; max-width: 100%" class="flex-grow-1 flex-shrink-0">
{{
CLINICAL_SIGNIFICANCE_LABEL[clinvarRecord?.referenceAssertions[0]!.clinicalSignificance]
}}
</v-col>
</v-row>

<v-row no-gutters class="flex-nowrap">
<v-col cols="1" class="font-weight-black"> Review Status </v-col>
<v-col
cols="1"
style="min-width: 100px; max-width: 100%"
class="flex-grow-1 flex-shrink-0 text-no-wrap"
>
<span v-for="i of [1, 2, 3, 4, 5]" :key="i">
<span
v-if="i <= REVIEW_STATUS_STARS[clinvarRecord?.referenceAssertions[0]!.reviewStatus]"
>
<v-icon>mdi-star</v-icon>
</span>
<span v-else>
<v-icon>mdi-star-outline</v-icon>
</span>
</span>
<span class="ml-3">
{{ REVIEW_STATUS_LABEL[clinvarRecord?.referenceAssertions[0]!.reviewStatus] }}
</span>
</v-col>
</v-row>

<v-row no-gutters class="flex-nowrap">
<v-col cols="1" class="font-weight-black"> Accession </v-col>
<v-col cols="1" style="min-width: 100px; max-width: 100%" class="flex-grow-1 flex-shrink-0">
<a
:href="`https://www.ncbi.nlm.nih.gov/clinvar/variation/${vcvToNumber(
clinvarRecord.vcv
)}/?redir=vcv`"
target="_blank"
>
<v-icon>mdi-launch</v-icon>
{{ clinvarRecord.vcv }}
</a>
</v-col>
</v-row>
</v-card-text>

<v-expand-transition>
<div v-show="expand">
<v-divider />

<v-card-title> ClinVar Reference Assertions </v-card-title>
<v-card-text>
<p>Below are all reference assertions for all assessed conditions from ClinVar.</p>
<v-table density="compact" class="mt-3">
<thead>
<tr>
<th>Condition</th>
<th>Significance</th>
<th>Review Status</th>
<th>RCV</th>
</tr>
</thead>
<tbody>
<tr v-for="assertion of clinvarRecord?.referenceAssertions" :key="assertion.rcv">
<td>
{{ assertion.title.split('AND')[1] }}
</td>
<td>
{{ CLINICAL_SIGNIFICANCE_LABEL[assertion.clinicalSignificance] }}
</td>
<td>
<span v-for="i of [1, 2, 3, 4, 5]" :key="i">
<span v-if="i <= REVIEW_STATUS_STARS[assertion.reviewStatus]">
<v-icon>mdi-star</v-icon>
</span>
<span v-else>
<v-icon>mdi-star-outline</v-icon>
</span>
</span>
<span class="ml-3"> {{ REVIEW_STATUS_LABEL[assertion.reviewStatus] }} </span>
</td>
<td>
<a
:href="`https://www.ncbi.nlm.nih.gov/clinvar/${assertion.rcv}`"
target="_blank"
>
<v-icon>mdi-launch</v-icon>
{{ assertion.rcv }}
</a>
</td>
</tr>
</tbody>
</v-table>
</v-card-text>
</div>
</v-expand-transition>

<v-divider />

<v-card-actions>
<v-btn @click="expand = !expand">
{{ !expand ? 'Show Reference Assertions' : 'Hide Reference Assertions' }}
</v-btn>
<v-spacer />
<v-btn :icon="expand ? 'mdi-chevron-up' : 'mdi-chevron-down'" @click="expand = !expand" />
</v-card-actions>
</v-card>

<v-card v-else variant="elevated">
<v-card-text> No ClinVar information available. </v-card-text>
</v-card>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SeqvarResultEntry } from '../../api/mehari/types'
import DocsLink from '../DocsLink/DocsLink.vue'
/** This component's */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = withDefaults(
defineProps<{
/** The variant consequences. */
Expand Down

0 comments on commit 1cc2b6d

Please sign in to comment.