diff --git a/frontend/src/assets/__tests__/BRCA1ClinVar.json b/frontend/src/assets/__tests__/BRCA1ClinVar.json index 21540484..43dd9ccd 100644 --- a/frontend/src/assets/__tests__/BRCA1ClinVar.json +++ b/frontend/src/assets/__tests__/BRCA1ClinVar.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afb5af6d54e234aea805cdc77a3c9cf304c99af9cfb9afc21f66980af14ce714 -size 13940534 +oid sha256:139fc291b0cf0eda6492b233c957b5ca9a19d32697ef834cfb642d8144bf7d31 +size 4410 diff --git a/frontend/src/assets/__tests__/BRCA1GeneInfo.json b/frontend/src/assets/__tests__/BRCA1GeneInfo.json index a771280d..6bac4916 100644 --- a/frontend/src/assets/__tests__/BRCA1GeneInfo.json +++ b/frontend/src/assets/__tests__/BRCA1GeneInfo.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ccd9cc6fd02367334835cf4c973d4ebf9faf02b84fd783c6830f962f8b20f5f -size 744847 +oid sha256:061a51397f44312651ef74202ae90b8f875c9e763dead656091abcd89120b78c +size 29896 diff --git a/frontend/src/assets/__tests__/BRCA1Transcripts.json b/frontend/src/assets/__tests__/BRCA1Transcripts.json index be13442f..6c857b3e 100644 --- a/frontend/src/assets/__tests__/BRCA1Transcripts.json +++ b/frontend/src/assets/__tests__/BRCA1Transcripts.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f78007f8bab23d77eb0ddb8d67641e0962a0af6cf5b4fb490a4a3f8a9b2e4a86 -size 143647 +oid sha256:56ab4badde9a9ec91d59b308207c38f7205fc48b40674ecc6b3a3e2f048ae8c6 +size 9790 diff --git a/frontend/src/components/GeneDetails/AlternativeIdentifiers.vue b/frontend/src/components/GeneDetails/AlternativeIdentifiers.vue new file mode 100644 index 00000000..019f0a6f --- /dev/null +++ b/frontend/src/components/GeneDetails/AlternativeIdentifiers.vue @@ -0,0 +1,84 @@ + + diff --git a/frontend/src/components/ClinVarFreqPlot.vue b/frontend/src/components/GeneDetails/ClinVarFreqPlot.vue similarity index 77% rename from frontend/src/components/ClinVarFreqPlot.vue rename to frontend/src/components/GeneDetails/ClinVarFreqPlot.vue index 6a477fab..d3327622 100644 --- a/frontend/src/components/ClinVarFreqPlot.vue +++ b/frontend/src/components/GeneDetails/ClinVarFreqPlot.vue @@ -119,17 +119,24 @@ const vegaEncoding = { diff --git a/frontend/src/components/GeneDetails/ClinvarImpact.vue b/frontend/src/components/GeneDetails/ClinvarImpact.vue new file mode 100644 index 00000000..20c58590 --- /dev/null +++ b/frontend/src/components/GeneDetails/ClinvarImpact.vue @@ -0,0 +1,110 @@ + + + diff --git a/frontend/src/components/GeneDetails/ConstraintsCard.vue b/frontend/src/components/GeneDetails/ConstraintsCard.vue new file mode 100644 index 00000000..f065aded --- /dev/null +++ b/frontend/src/components/GeneDetails/ConstraintsCard.vue @@ -0,0 +1,74 @@ + + diff --git a/frontend/src/components/GeneDetails/DiseaseAnnotation.vue b/frontend/src/components/GeneDetails/DiseaseAnnotation.vue new file mode 100644 index 00000000..7abc4697 --- /dev/null +++ b/frontend/src/components/GeneDetails/DiseaseAnnotation.vue @@ -0,0 +1,42 @@ + + + diff --git a/frontend/src/components/GeneDetails/ExternalResources.vue b/frontend/src/components/GeneDetails/ExternalResources.vue new file mode 100644 index 00000000..7b06ec19 --- /dev/null +++ b/frontend/src/components/GeneDetails/ExternalResources.vue @@ -0,0 +1,130 @@ + + diff --git a/frontend/src/components/GeneDetails/GeneRifs.vue b/frontend/src/components/GeneDetails/GeneRifs.vue new file mode 100644 index 00000000..82a2deae --- /dev/null +++ b/frontend/src/components/GeneDetails/GeneRifs.vue @@ -0,0 +1,37 @@ + + + diff --git a/frontend/src/components/GtexGenePlot.vue b/frontend/src/components/GeneDetails/GtexGenePlot.vue similarity index 78% rename from frontend/src/components/GtexGenePlot.vue rename to frontend/src/components/GeneDetails/GtexGenePlot.vue index a28a9947..f4f33061 100644 --- a/frontend/src/components/GtexGenePlot.vue +++ b/frontend/src/components/GeneDetails/GtexGenePlot.vue @@ -17,6 +17,8 @@ export interface Props { geneSymbol: string /** Expression records */ expressionRecords: ExpressionRecord[] + /** Ensembl gene ID */ + ensemblGeneId: string } const props = withDefaults(defineProps(), {}) @@ -171,17 +173,37 @@ const vegaLayer = [ diff --git a/frontend/src/components/GeneDetails/HgncCard.vue b/frontend/src/components/GeneDetails/HgncCard.vue new file mode 100644 index 00000000..678be359 --- /dev/null +++ b/frontend/src/components/GeneDetails/HgncCard.vue @@ -0,0 +1,40 @@ + + + diff --git a/frontend/src/components/GeneDetails/LocusDatabases.vue b/frontend/src/components/GeneDetails/LocusDatabases.vue new file mode 100644 index 00000000..9b62fa7a --- /dev/null +++ b/frontend/src/components/GeneDetails/LocusDatabases.vue @@ -0,0 +1,36 @@ + + + diff --git a/frontend/src/components/GeneDetails/NcbiSummary.vue b/frontend/src/components/GeneDetails/NcbiSummary.vue new file mode 100644 index 00000000..2eeb3a30 --- /dev/null +++ b/frontend/src/components/GeneDetails/NcbiSummary.vue @@ -0,0 +1,26 @@ + + + diff --git a/frontend/src/components/GeneDetails/SupplementaryList.vue b/frontend/src/components/GeneDetails/SupplementaryList.vue new file mode 100644 index 00000000..5f2e5e85 --- /dev/null +++ b/frontend/src/components/GeneDetails/SupplementaryList.vue @@ -0,0 +1,31 @@ + + + diff --git a/frontend/src/components/VariationLandscape.vue b/frontend/src/components/GeneDetails/VariationLandscape.vue similarity index 97% rename from frontend/src/components/VariationLandscape.vue rename to frontend/src/components/GeneDetails/VariationLandscape.vue index a4fe9881..81831a4e 100644 --- a/frontend/src/components/VariationLandscape.vue +++ b/frontend/src/components/GeneDetails/VariationLandscape.vue @@ -353,9 +353,13 @@ const vegaLayer = [ diff --git a/frontend/src/components/GenomeBrowser.vue b/frontend/src/components/GenomeBrowser.vue index 63ed1858..60a1fd4e 100644 --- a/frontend/src/components/GenomeBrowser.vue +++ b/frontend/src/components/GenomeBrowser.vue @@ -92,5 +92,9 @@ onMounted(() => { diff --git a/frontend/src/components/HeaderDefault.vue b/frontend/src/components/HeaderDefault.vue index 2029e54a..968448ae 100644 --- a/frontend/src/components/HeaderDefault.vue +++ b/frontend/src/components/HeaderDefault.vue @@ -46,7 +46,7 @@ import UserProfileButton from '@/components/UserProfileButton.vue' diff --git a/frontend/src/views/GeneDetailView.vue b/frontend/src/views/GeneDetailView.vue index 814d1141..26388aa0 100644 --- a/frontend/src/views/GeneDetailView.vue +++ b/frontend/src/views/GeneDetailView.vue @@ -1,12 +1,21 @@ diff --git a/frontend/src/views/TermsView.vue b/frontend/src/views/TermsView.vue index e853e459..ed6dd5c5 100644 --- a/frontend/src/views/TermsView.vue +++ b/frontend/src/views/TermsView.vue @@ -7,16 +7,21 @@ import HeaderDefault from '@/components/HeaderDefault.vue' -

Terms of Use

-

REEV is for research use only software

-

- The software is provided "as is," without warranty of any kind, express or implied, - including but not limited to the warranties of merchantability, fitness for a particular - purpose, and noninfringement. In no event shall the authors or copyright holders be liable - for any claim, damages, or other liability, whether in an action of contract, tort, or - otherwise, arising from, out of, or in connection with the software or the use or other - dealings in the software. -

+ + Terms of Use + + +

REEV is for research use only software

+

+ The software is provided "as is," without warranty of any kind, express or implied, + including but not limited to the warranties of merchantability, fitness for a + particular purpose, and noninfringement. In no event shall the authors or copyright + holders be liable for any claim, damages, or other liability, whether in an action of + contract, tort, or otherwise, arising from, out of, or in connection with the software + or the use or other dealings in the software. +

@@ -27,12 +32,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue' padding: 40px; } -.title { - font-size: 24px; - font-weight: bold; - margin-bottom: 20px; -} - p { font-size: 16px; margin-bottom: 20px; diff --git a/frontend/src/views/VariantDetailView.vue b/frontend/src/views/VariantDetailView.vue index 6a74aed8..efac1e93 100644 --- a/frontend/src/views/VariantDetailView.vue +++ b/frontend/src/views/VariantDetailView.vue @@ -116,33 +116,33 @@ const performSearch = async (geneSymbol: string) => {
-
-

Gene

-

- Link to - {{ - variantInfoStore.varAnnos?.cadd?.GeneName - }} -

- - -
-
-

Gene

-

No gene information available

+
+ + Gene + + Link to + {{ + variantInfoStore.varAnnos?.cadd?.GeneName + }} + + + + + + Gene + No gene information available +
-

Beacon Network

-
- +
+ +
-
-

Population Frequencies

- +
{
-

Variant Tools

- {
-

ACMG Rating

-
-

Consequences

-
-

Consequences

-

No consequence information available

+ + Consequence + No consequence information available. +
{ id="conservation" class="variant-item" > -

Conservation

-
-

Conservation

-

No conservation information available

+ + Conservation + No conservation information available. +
-

Variant Validator

- @@ -215,8 +209,8 @@ const performSearch = async (geneSymbol: string) => { .variant-item { margin-bottom: 20px; - border: 2px solid rgb(229, 85, 64); - border-radius: 10px; + /* border: 2px solid rgb(229, 85, 64); + border-radius: 10px; */ padding: 5px 10px; } diff --git a/frontend/src/views/__tests__/GeneDetailView.spec.ts b/frontend/src/views/__tests__/GeneDetailView.spec.ts index 3bb92d05..cd17d974 100644 --- a/frontend/src/views/__tests__/GeneDetailView.spec.ts +++ b/frontend/src/views/__tests__/GeneDetailView.spec.ts @@ -3,7 +3,19 @@ import { describe, expect, it, vi } from 'vitest' import { nextTick } from 'vue' import { VMenu } from 'vuetify/components' +import * as BRCA1ClinVar from '@/assets/__tests__/BRCA1ClinVar.json' import * as BRCA1geneInfo from '@/assets/__tests__/BRCA1GeneInfo.json' +import * as BRCA1Transcripts from '@/assets/__tests__/BRCA1Transcripts.json' +import AlternativeIdentifiers from '@/components/GeneDetails/AlternativeIdentifiers.vue' +import ClinVarFreqPlot from '@/components/GeneDetails/ClinVarFreqPlot.vue' +import ClinvarImpact from '@/components/GeneDetails/ClinvarImpact.vue' +import DiseaseAnnotation from '@/components/GeneDetails/DiseaseAnnotation.vue' +import ExternalResources from '@/components/GeneDetails/ExternalResources.vue' +import GeneRifs from '@/components/GeneDetails/GeneRifs.vue' +import LocusDatabases from '@/components/GeneDetails/LocusDatabases.vue' +import NcbiSummary from '@/components/GeneDetails/NcbiSummary.vue' +import SupplementaryList from '@/components/GeneDetails/SupplementaryList.vue' +import VariationLandscape from '@/components/GeneDetails/VariationLandscape.vue' import HeaderDetailPage from '@/components/HeaderDetailPage.vue' import SearchBar from '@/components/SearchBar.vue' import { setupMountedComponents } from '@/lib/test-utils' @@ -13,24 +25,30 @@ import { StoreState } from '@/stores/misc' import GeneDetailView from '../GeneDetailView.vue' const geneData = { - storeState: 'active', - geneSymbol: 'BRCA1', - geneInfo: BRCA1geneInfo.genes['HGNC:1100'] + storeState: StoreState.Active, + geneSymbol: 'HGNC:1100', + geneInfo: JSON.parse(JSON.stringify(BRCA1geneInfo)).genes['HGNC:1100'], + geneClinvar: JSON.parse(JSON.stringify(BRCA1ClinVar)).genes['HGNC:1100'], + transcripts: JSON.parse(JSON.stringify(BRCA1Transcripts)) } -const makeWrapper = (geneDataExample: Object) => { +const makeWrapper = () => { const pinia = createTestingPinia({ createSpy: vi.fn }) - const store = useGeneInfoStore(pinia) + const geneInfoStore = useGeneInfoStore(pinia) const mockLoadData = vi.fn().mockImplementation(async (geneSymbol: string) => { - store.storeState = StoreState.Active - store.geneSymbol = geneSymbol - store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + geneInfoStore.storeState = StoreState.Active + geneInfoStore.geneSymbol = geneSymbol + geneInfoStore.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + geneInfoStore.geneClinvar = JSON.parse(JSON.stringify(geneData.geneClinvar)) + geneInfoStore.transcripts = JSON.parse(JSON.stringify(geneData.transcripts)) }) - store.loadData = mockLoadData + geneInfoStore.loadData = mockLoadData - store.storeState = StoreState.Active - store.geneSymbol = geneData.geneSymbol - store.geneInfo = JSON.parse(JSON.stringify(geneDataExample)) + geneInfoStore.storeState = StoreState.Active + geneInfoStore.geneSymbol = geneData.geneSymbol + geneInfoStore.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + geneInfoStore.geneClinvar = JSON.parse(JSON.stringify(geneData.geneClinvar)) + geneInfoStore.transcripts = JSON.parse(JSON.stringify(geneData.transcripts)) return setupMountedComponents( { @@ -39,33 +57,17 @@ const makeWrapper = (geneDataExample: Object) => { }, { props: { - searchTerm: 'BRCA1' + searchTerm: 'HGNC:1100', + genomeRelease: 'grch37' }, pinia: pinia } ) } -describe.concurrent('GeneDetailView', async () => { - it('renders the page with invalid data', async () => { - const { wrapper } = makeWrapper(geneData) - - const header = wrapper.findComponent(HeaderDetailPage) - const searchBar = wrapper.findComponent(SearchBar) - expect(header.exists()).toBe(true) - expect(searchBar.exists()).toBe(true) - - const logo = wrapper.find('#logo') - const menu = wrapper.findComponent(VMenu) - expect(logo.exists()).toBe(true) - expect(menu.exists()).toBe(true) - - const launchImage = wrapper.findAll('.mdi-launch') - expect(launchImage.length).toBe(14) - }) - +describe('GeneDetailView', async () => { it('renders the header', async () => { - const { wrapper } = makeWrapper(geneData.geneInfo) + const { wrapper } = makeWrapper() const header = wrapper.findComponent(HeaderDetailPage) const searchBar = wrapper.findComponent(SearchBar) @@ -79,7 +81,7 @@ describe.concurrent('GeneDetailView', async () => { }) it('renders info-cards and navigation drawer', () => { - const { wrapper } = makeWrapper(geneData.geneInfo) + const { wrapper } = makeWrapper() const navigationDrawer = wrapper.find('.v-navigation-drawer') expect(navigationDrawer.exists()).toBe(true) @@ -103,12 +105,31 @@ describe.concurrent('GeneDetailView', async () => { expect(geneRifs.exists()).toBe(true) expect(locusSpecificDatabases.exists()).toBe(true) - const launchImage = wrapper.findAll('.mdi-launch') - expect(launchImage.length).toBe(2573) + // Renders the main content + const alternativeIdentifiersCard = wrapper.findComponent(AlternativeIdentifiers) + const externalResourcesCard = wrapper.findComponent(ExternalResources) + const clinVarFreqPlotCard = wrapper.findComponent(ClinVarFreqPlot) + const clinVarImpactCard = wrapper.findComponent(ClinvarImpact) + const diseaseAnnotationCard = wrapper.findComponent(DiseaseAnnotation) + const geneRifsCard = wrapper.findComponent(GeneRifs) + const locusDatabasesCard = wrapper.findComponent(LocusDatabases) + const ncbiSummaryCard = wrapper.findComponent(NcbiSummary) + const supplementaryListCard = wrapper.findComponent(SupplementaryList) + const variationLandscapeCard = wrapper.findComponent(VariationLandscape) + expect(alternativeIdentifiersCard.exists()).toBe(true) + expect(externalResourcesCard.exists()).toBe(true) + expect(clinVarFreqPlotCard.exists()).toBe(true) + expect(clinVarImpactCard.exists()).toBe(true) + expect(diseaseAnnotationCard.exists()).toBe(true) + expect(geneRifsCard.exists()).toBe(true) + expect(locusDatabasesCard.exists()).toBe(true) + expect(ncbiSummaryCard.exists()).toBe(true) + expect(supplementaryListCard.exists()).toBe(true) + expect(variationLandscapeCard.exists()).toBe(true) }) it('emits update in header', async () => { - const { wrapper } = makeWrapper(geneData.geneInfo) + const { wrapper } = makeWrapper() const header = wrapper.findComponent(HeaderDetailPage) expect(header.exists()).toBe(true) @@ -128,7 +149,7 @@ describe.concurrent('GeneDetailView', async () => { }) it('emits scroll to section', async () => { - const { wrapper, router } = makeWrapper(geneData.geneInfo) + const { wrapper, router } = makeWrapper() const hgncLink = wrapper.find('#hgnc-nav') expect(hgncLink.exists()).toBe(true) @@ -153,11 +174,15 @@ describe.concurrent('GeneDetailView', async () => { store.storeState = StoreState.Error store.geneSymbol = geneSymbol store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + store.geneClinvar = JSON.parse(JSON.stringify(geneData.geneClinvar)) + store.transcripts = JSON.parse(JSON.stringify(geneData.transcripts)) }) const mockClearData = vi.fn().mockImplementation(() => { store.storeState = StoreState.Initial store.geneSymbol = '' store.geneInfo = {} + store.geneClinvar = {} + store.transcripts = {} }) store.loadData = mockLoadData store.clearData = mockClearData @@ -165,6 +190,8 @@ describe.concurrent('GeneDetailView', async () => { store.storeState = StoreState.Active store.geneSymbol = geneData.geneSymbol store.geneInfo = JSON.parse(JSON.stringify(geneData.geneInfo)) + store.geneClinvar = JSON.parse(JSON.stringify(geneData.geneClinvar)) + store.transcripts = JSON.parse(JSON.stringify(geneData.transcripts)) const { router } = setupMountedComponents( { @@ -173,7 +200,8 @@ describe.concurrent('GeneDetailView', async () => { }, { props: { - searchTerm: 'BRCA1' + searchTerm: 'HGNC:1100', + genomeRelease: 'grch37' }, pinia: pinia } diff --git a/frontend/src/views/__tests__/HomeView.spec.ts b/frontend/src/views/__tests__/HomeView.spec.ts index d2f123e3..1b44449f 100644 --- a/frontend/src/views/__tests__/HomeView.spec.ts +++ b/frontend/src/views/__tests__/HomeView.spec.ts @@ -98,13 +98,13 @@ describe.concurrent('HomeView with mocked router', async () => { } ) - const subtitle = wrapper.find('h2') const exampleTerms = wrapper.findAll('.example') - expect(subtitle.exists()).toBe(true) expect(exampleTerms.length).toBe(8) }) - it('uses example by click', async () => { + it('searches for example by click', async () => { + vi.spyOn(DottyClient.prototype, 'toSpdi').mockResolvedValue(null) + const { wrapper } = setupMountedComponents( { component: HomeView, template: true }, {