diff --git a/src/api/mehari/fixture.seqvarCsqResponse.BRCA1.json b/src/api/mehari/fixture.seqvarCsqResponse.BRCA1.json index 64d3a61..594d006 100644 --- a/src/api/mehari/fixture.seqvarCsqResponse.BRCA1.json +++ b/src/api/mehari/fixture.seqvarCsqResponse.BRCA1.json @@ -11,6 +11,7 @@ "result": [ { "consequences": ["MissenseVariant"], + "feature_tag": ["ManeSelect"], "putative_impact": "Moderate", "gene_symbol": "BRCA1", "gene_id": "HGNC:1100", diff --git a/src/api/mehari/types.ts b/src/api/mehari/types.ts index 2969699..778d41e 100644 --- a/src/api/mehari/types.ts +++ b/src/api/mehari/types.ts @@ -72,6 +72,44 @@ export enum SeqvarConsequence { UpstreamGeneVariant = 'upstream_gene_variant' } +/** + * Interface for the rank as returned by API. + */ +export interface Rank$Api { + /** The exon / intron number. */ + rank: number + /** The total number of exons / introns. */ + total: number +} + +/** + * Interface for the rank. + */ +export interface Rank { + /** The exon / intron number. */ + rank: number + /** The total number of exons / introns. */ + total: number +} + +/** + * Helper class for converting `Rank$Api` to `Rank`. + */ +class Rank$Type { + /** Convert `Rank$Api` to `Rank`. */ + fromJson(json: Rank$Api): Rank { + return { + rank: json.rank, + total: json.total + } + } +} + +/** + * Helper for converting `Rank$Api` to `Rank`. + */ +export const Rank: Rank$Type = new Rank$Type() + /** * Interface for one entry in the result as returned by API. */ @@ -93,7 +131,7 @@ export interface SeqvarResultEntry$Api { /** The feature tags. */ feature_tag: string[] /** The exon / intron rank. */ - rank?: number + rank?: Rank$Api /** HGVS c. notation. */ hgvs_t?: string /** HGVS p. notation. */ @@ -131,7 +169,7 @@ export interface SeqvarResultEntry { /** The feature tags. */ featureTag: string[] /** The exon / intron rank. */ - rank?: number + rank?: Rank /** HGVS c. notation. */ hgvsT?: string /** HGVS p. notation. */ @@ -163,7 +201,7 @@ class SeqvarResultEntry$Type { featureId: json.feature_id, featureBiotype: json.feature_biotype, featureTag: json.feature_tag, - rank: json.rank, + rank: json.rank === undefined ? undefined : Rank.fromJson(json.rank), hgvsT: json.hgvs_t, hgvsP: json.hgvs_p, txPos: json.tx_pos, diff --git a/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.spec.ts b/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.spec.ts new file mode 100644 index 0000000..505fab0 --- /dev/null +++ b/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.spec.ts @@ -0,0 +1,45 @@ +import fs from 'fs' +import path from 'path' +import { describe, expect, it } from 'vitest' + +import { SeqvarResult } from '../../api/mehari/types' +import { setupMountedComponents } from '../../lib/testUtils' +import SeqvarConsequencesCard from './SeqvarConsequencesCard.vue' + +/** Fixtures */ +const seqvarCsqResult = SeqvarResult.fromJson( + JSON.parse( + fs.readFileSync( + path.resolve(__dirname, '../../api/mehari/fixture.seqvarCsqResponse.BRCA1.json'), + 'utf-8' + ) + ) +) + +describe.concurrent('SeqvarConsequencesCard.vue', async () => { + it('renders the consequence info', async () => { + // arrange: + const { wrapper } = await setupMountedComponents( + { component: SeqvarConsequencesCard }, + { + props: { + consequences: seqvarCsqResult.result + } + } + ) + + // act: nothing, only test rendering + + // assert: + const table = wrapper.find('table') + expect(table.exists()).toBe(true) + const headers = table.findAll('th') + expect(headers.length).toBe(6) + expect(headers[0].text()).toBe('Gene') + expect(headers[1].text()).toBe('Transcript') + expect(headers[2].text()).toBe('Consequence') + expect(headers[3].text()).toBe('HGVS.p') + expect(headers[4].text()).toBe('HGVS.t') + expect(headers[5].text()).toBe('Exon/Intron') + }) +}) diff --git a/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.stories.ts b/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.stories.ts new file mode 100644 index 0000000..38e70d0 --- /dev/null +++ b/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.stories.ts @@ -0,0 +1,31 @@ +import { JsonValue } from '@protobuf-ts/runtime' +import type { Meta, StoryObj } from '@storybook/vue3' + +import seqvarCsqResultBrca1Json from '../../api/mehari/fixture.seqvarCsqResponse.BRCA1.json' +import { SeqvarResult } from '../../api/mehari/types' +import SeqvarConsequencesCard from './SeqvarConsequencesCard.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 seqvarCsqResponseBrca1 = SeqvarResult.fromJson(seqvarCsqResultBrca1Json as JsonValue) + +const meta = { + title: 'Seqvar/SeqvarConsequencesCard', + component: SeqvarConsequencesCard, + tags: ['autodocs'], + argTypes: { + consequences: { control: { type: 'object' } } + }, + args: { consequences: seqvarCsqResponseBrca1.result } +} satisfies Meta + +export default meta +type Story = StoryObj + +export const BRCA1: Story = { + args: { + consequences: seqvarCsqResponseBrca1.result + } +} diff --git a/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.vue b/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.vue new file mode 100644 index 0000000..96cdf39 --- /dev/null +++ b/src/components/SeqvarConsequencesCard/SeqvarConsequencesCard.vue @@ -0,0 +1,85 @@ + + +