@molgenis/vip-report-template 6.0.1 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +0 -3
- package/.travis.yml +5 -2
- package/index.html +1 -1
- package/package.json +23 -23
- package/src/App.tsx +27 -68
- package/src/__tests__/sample.test.ts +184 -0
- package/src/assets/sass/main.scss +12 -47
- package/src/components/Breadcrumb.tsx +6 -6
- package/src/components/InfoCollapsablePane.tsx +4 -4
- package/src/components/SampleTable.tsx +37 -46
- package/src/components/VariantInfoNestedTable.tsx +3 -3
- package/src/components/VariantsSampleTable.tsx +21 -44
- package/src/components/VariantsTable.tsx +2 -3
- package/src/components/filter/Filter.tsx +0 -4
- package/src/components/record/format/GenotypeField.tsx +4 -15
- package/src/index.tsx +43 -1
- package/src/store/index.tsx +7 -10
- package/src/utils/sample.ts +63 -3
- package/src/views/Help.tsx +131 -6
- package/src/views/Home.tsx +24 -112
- package/src/views/Sample.tsx +21 -17
- package/src/views/SampleVariant.tsx +36 -21
- package/src/views/SampleVariantConsequence.tsx +44 -33
- package/src/views/SampleVariants.tsx +81 -10
- package/src/views/Samples.tsx +35 -33
- package/src/views/Variant.tsx +48 -42
- package/src/views/VariantConsequence.tsx +9 -8
- package/src/views/Variants.tsx +8 -2
- package/src/views/data/data.tsx +7 -0
- package/src/components/filter/FilterIntegerDp.tsx +0 -47
- package/src/views/data/SampleData.tsx +0 -13
- package/src/views/data/SampleVariantConsequenceData.tsx +0 -13
- package/src/views/data/SampleVariantData.tsx +0 -14
- package/src/views/data/VariantConsequenceData.tsx +0 -10
- package/src/views/data/VariantData.tsx +0 -13
package/src/views/Home.tsx
CHANGED
|
@@ -1,121 +1,33 @@
|
|
|
1
|
-
import { Component
|
|
1
|
+
import { Component } from "solid-js";
|
|
2
2
|
import { Breadcrumb } from "../components/Breadcrumb";
|
|
3
|
-
import {
|
|
4
|
-
import { HpoTerm } from "../components/HpoTerm";
|
|
5
|
-
import {
|
|
6
|
-
EMPTY_APP_METADATA,
|
|
7
|
-
EMPTY_HTS_FILE_METADATA,
|
|
8
|
-
EMPTY_PARAMS,
|
|
9
|
-
EMPTY_PHENOTYPES,
|
|
10
|
-
EMPTY_RECORDS_METADATA,
|
|
11
|
-
EMPTY_RECORDS_PAGE,
|
|
12
|
-
EMPTY_SAMPLES_PAGE,
|
|
13
|
-
fetchAppMetadata,
|
|
14
|
-
fetchHtsFileMetadata,
|
|
15
|
-
fetchPhenotypes,
|
|
16
|
-
fetchRecords,
|
|
17
|
-
fetchRecordsMeta,
|
|
18
|
-
fetchSamples,
|
|
19
|
-
} from "../utils/ApiUtils";
|
|
20
|
-
import { getHeaderValue } from "../utils/viewUtils";
|
|
21
|
-
import { Loader } from "../components/Loader";
|
|
22
|
-
import { VcfHeaderRow } from "../components/VcfHeaderRow";
|
|
3
|
+
import { useNavigate } from "@solidjs/router";
|
|
23
4
|
|
|
24
5
|
export const Home: Component = () => {
|
|
25
|
-
const
|
|
26
|
-
const [samples] = createResource(params, fetchSamples, { initialValue: EMPTY_SAMPLES_PAGE });
|
|
27
|
-
const [records] = createResource(params, fetchRecords, { initialValue: EMPTY_RECORDS_PAGE });
|
|
28
|
-
const [recordsMetadata] = createResource(params, fetchRecordsMeta, { initialValue: EMPTY_RECORDS_METADATA });
|
|
29
|
-
const [phenotypes] = createResource(params, fetchPhenotypes, { initialValue: EMPTY_PHENOTYPES });
|
|
30
|
-
const [htsFileMetadata] = createResource(params, fetchHtsFileMetadata, { initialValue: EMPTY_HTS_FILE_METADATA });
|
|
31
|
-
const [appMetadata] = createResource(params, fetchAppMetadata, { initialValue: EMPTY_APP_METADATA });
|
|
6
|
+
const navigate = useNavigate();
|
|
32
7
|
|
|
33
8
|
return (
|
|
34
|
-
|
|
35
|
-
when={
|
|
36
|
-
!samples.loading &&
|
|
37
|
-
!records.loading &&
|
|
38
|
-
!recordsMetadata.loading &&
|
|
39
|
-
!phenotypes.loading &&
|
|
40
|
-
!htsFileMetadata.loading &&
|
|
41
|
-
!appMetadata.loading
|
|
42
|
-
}
|
|
43
|
-
fallback={<Loader />}
|
|
44
|
-
>
|
|
9
|
+
<>
|
|
45
10
|
<Breadcrumb items={[]} />
|
|
46
|
-
<div class="
|
|
47
|
-
<
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
</tr>
|
|
66
|
-
<VcfHeaderRow value={getHeaderValue("VIP_Version", recordsMetadata().lines)} title={"VIP Version"} />
|
|
67
|
-
<VcfHeaderRow value={getHeaderValue("VIP_Command", recordsMetadata().lines)} title={"VIP Command"} />
|
|
68
|
-
</tbody>
|
|
69
|
-
</table>
|
|
11
|
+
<div class="columns is-centered">
|
|
12
|
+
<div class="column is-three-quarters-widescreen">
|
|
13
|
+
<p class="title is-2">Report</p>
|
|
14
|
+
<p class="subtitle is-4">
|
|
15
|
+
Analyze annotated, classified and filtered variants to solve rare-disease patients
|
|
16
|
+
</p>
|
|
17
|
+
<div class="columns">
|
|
18
|
+
<div class="column">
|
|
19
|
+
<div class="buttons are-large">
|
|
20
|
+
<button class="button is-large" onClick={() => navigate("/variants")}>
|
|
21
|
+
Explore Variants without samples
|
|
22
|
+
</button>
|
|
23
|
+
<button class="button is-large is-primary" onClick={() => navigate("/samples")}>
|
|
24
|
+
Explore Variants for samples
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
70
30
|
</div>
|
|
71
|
-
|
|
72
|
-
<table class="table is-narrow">
|
|
73
|
-
<thead>
|
|
74
|
-
<tr>
|
|
75
|
-
<th colSpan={2}>Input metadata</th>
|
|
76
|
-
</tr>
|
|
77
|
-
</thead>
|
|
78
|
-
<tbody>
|
|
79
|
-
<tr>
|
|
80
|
-
<th>Filename:</th>
|
|
81
|
-
<td>{htsFileMetadata().uri}</td>
|
|
82
|
-
</tr>
|
|
83
|
-
<tr>
|
|
84
|
-
<th>Assembly:</th>
|
|
85
|
-
<td>{htsFileMetadata().genomeAssembly}</td>
|
|
86
|
-
</tr>
|
|
87
|
-
<tr>
|
|
88
|
-
<th>Number of records:</th>
|
|
89
|
-
<td>{records().total}</td>
|
|
90
|
-
</tr>
|
|
91
|
-
<tr>
|
|
92
|
-
<th>Number of samples:</th>
|
|
93
|
-
<td>{samples().total}</td>
|
|
94
|
-
</tr>
|
|
95
|
-
<tr>
|
|
96
|
-
<th>Phenotypes:</th>
|
|
97
|
-
<td>
|
|
98
|
-
<For each={phenotypes().items}>
|
|
99
|
-
{(item: Item<Phenotype>) => (
|
|
100
|
-
<>
|
|
101
|
-
<b>{item.data.subject.id}: </b>
|
|
102
|
-
<For each={item.data.phenotypicFeaturesList}>
|
|
103
|
-
{(phenotypicFeature: PhenotypicFeature, i) => (
|
|
104
|
-
<>
|
|
105
|
-
{i() > 0 ? ", " : ""}
|
|
106
|
-
<HpoTerm ontologyClass={phenotypicFeature.type} />
|
|
107
|
-
</>
|
|
108
|
-
)}
|
|
109
|
-
</For>
|
|
110
|
-
<br />
|
|
111
|
-
</>
|
|
112
|
-
)}
|
|
113
|
-
</For>
|
|
114
|
-
</td>
|
|
115
|
-
</tr>
|
|
116
|
-
</tbody>
|
|
117
|
-
</table>
|
|
118
|
-
</div>
|
|
119
|
-
</Show>
|
|
31
|
+
</>
|
|
120
32
|
);
|
|
121
33
|
};
|
package/src/views/Sample.tsx
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import { Component, For, Show } from "solid-js";
|
|
2
|
-
import {
|
|
2
|
+
import { createAsync, RouteSectionProps } from "@solidjs/router";
|
|
3
3
|
import { Loader } from "../components/Loader";
|
|
4
4
|
import { Breadcrumb } from "../components/Breadcrumb";
|
|
5
|
-
import {
|
|
5
|
+
import { getSample } from "./data/data";
|
|
6
6
|
|
|
7
|
-
export const Sample: Component = () => {
|
|
8
|
-
const
|
|
7
|
+
export const Sample: Component<RouteSectionProps> = (props) => {
|
|
8
|
+
const sample = createAsync(() => getSample(Number(props.params.sampleId)));
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
|
-
<Show when={
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
<Show when={sample()} fallback={<Loader />}>
|
|
12
|
+
{(sample) => (
|
|
13
|
+
<>
|
|
14
|
+
<Breadcrumb items={[{ href: "/samples", text: "Samples" }, { text: sample().data.person.individualId }]} />
|
|
15
|
+
<p class="has-text-weight-semibold">Sample</p>
|
|
16
|
+
<div class="columns">
|
|
17
|
+
<div class="column is-1">
|
|
18
|
+
<For each={Object.keys(sample().data.person)}>{(key) => <p>{key}</p>}</For>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="column is-1">
|
|
21
|
+
<For each={Object.values(sample().data.person)}>{(value) => <p>{value}</p>}</For>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<br />
|
|
25
|
+
<a href={`/samples/${sample().id}/variants`}>Variants</a>
|
|
26
|
+
</>
|
|
27
|
+
)}
|
|
24
28
|
</Show>
|
|
25
29
|
);
|
|
26
30
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, createResource, For, Show } from "solid-js";
|
|
2
|
-
import {
|
|
2
|
+
import { createAsync, RouteSectionProps } from "@solidjs/router";
|
|
3
3
|
import { Loader } from "../components/Loader";
|
|
4
4
|
import { GenomeBrowser } from "../components/GenomeBrowser";
|
|
5
5
|
import { fetchPedigreeSamples, fetchRecordsMeta, getRecordLabel } from "../utils/ApiUtils";
|
|
@@ -11,36 +11,51 @@ import { getNestedInfoFieldsWithValues } from "../utils/field";
|
|
|
11
11
|
import { VariantSampleTable } from "../components/VariantSampleTable";
|
|
12
12
|
import { Breadcrumb } from "../components/Breadcrumb";
|
|
13
13
|
import { getRecordSamples } from "../utils/viewUtils";
|
|
14
|
-
import { SampleVariantRouteData } from "./data/SampleVariantData";
|
|
15
14
|
import { getSampleLabel } from "../utils/sample";
|
|
16
15
|
import { Item, Sample } from "@molgenis/vip-report-api/src/Api";
|
|
17
16
|
import { Metadata, Record } from "@molgenis/vip-report-vcf/src/Vcf";
|
|
18
17
|
import { Abbr } from "../components/Abbr";
|
|
18
|
+
import { getSample, getVariant } from "./data/data";
|
|
19
19
|
|
|
20
|
-
export const SampleVariantView: Component = () => {
|
|
21
|
-
const
|
|
20
|
+
export const SampleVariantView: Component<RouteSectionProps> = (props) => {
|
|
21
|
+
const sample = createAsync(() => getSample(Number(props.params.sampleId)));
|
|
22
|
+
const variant = createAsync(() => getVariant(Number(props.params.variantId)));
|
|
22
23
|
|
|
23
24
|
const [pedigreeSamples] = createResource(sample, fetchPedigreeSamples);
|
|
24
25
|
const [recordsMeta] = createResource(fetchRecordsMeta);
|
|
25
26
|
|
|
26
27
|
return (
|
|
27
|
-
<Show when={sample()
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
<Show when={sample()} fallback={<Loader />}>
|
|
29
|
+
{(sample) => (
|
|
30
|
+
<Show when={variant()} fallback={<Loader />}>
|
|
31
|
+
{(variant) => (
|
|
32
|
+
<>
|
|
33
|
+
<Breadcrumb
|
|
34
|
+
items={[
|
|
35
|
+
{ href: "/samples", text: "Samples" },
|
|
36
|
+
{ href: `/samples/${sample().id}`, text: getSampleLabel(sample().data) },
|
|
37
|
+
{ href: `/samples/${sample().id}/variants`, text: "Variants" },
|
|
38
|
+
{ text: getRecordLabel(variant()) },
|
|
39
|
+
]}
|
|
40
|
+
/>
|
|
41
|
+
<Show when={pedigreeSamples()} fallback={<Loader />}>
|
|
42
|
+
{(pedigreeSamples) => (
|
|
43
|
+
<Show when={recordsMeta()} fallback={<Loader />}>
|
|
44
|
+
{(recordsMeta) => (
|
|
45
|
+
<SampleVariant
|
|
46
|
+
sample={sample()}
|
|
47
|
+
pedigreeSamples={pedigreeSamples().items}
|
|
48
|
+
recordsMeta={recordsMeta()}
|
|
49
|
+
record={variant()}
|
|
50
|
+
/>
|
|
51
|
+
)}
|
|
52
|
+
</Show>
|
|
53
|
+
)}
|
|
54
|
+
</Show>
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
</Show>
|
|
58
|
+
)}
|
|
44
59
|
</Show>
|
|
45
60
|
);
|
|
46
61
|
};
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Component, createResource, Show } from "solid-js";
|
|
2
|
-
import {
|
|
2
|
+
import { createAsync, RouteSectionProps } from "@solidjs/router";
|
|
3
3
|
import { Loader } from "../components/Loader";
|
|
4
|
-
import {
|
|
5
|
-
fetchDecisionTree,
|
|
6
|
-
fetchHtsFileMetadata,
|
|
7
|
-
fetchPedigreeSamples,
|
|
8
|
-
fetchRecordsMeta,
|
|
9
|
-
getRecordLabel,
|
|
10
|
-
} from "../utils/ApiUtils";
|
|
4
|
+
import { fetchDecisionTree, fetchPedigreeSamples, fetchRecordsMeta, getRecordLabel } from "../utils/ApiUtils";
|
|
11
5
|
import { VariantTable } from "../components/VariantTable";
|
|
12
6
|
import { VariantInfoTable } from "../components/VariantInfoTable";
|
|
13
7
|
import { VariantSampleTable } from "../components/VariantSampleTable";
|
|
@@ -16,41 +10,58 @@ import { ConsequenceTable } from "../components/ConsequenceTable";
|
|
|
16
10
|
import { getRecordSamples, getSpecificConsequence } from "../utils/viewUtils";
|
|
17
11
|
import { DecisionTreePath } from "../components/tree/DecisionTreePath";
|
|
18
12
|
import { getDecisionTreePath } from "../utils/decisionTreeUtils";
|
|
19
|
-
import { SampleVariantConsequenceRouteData } from "./data/SampleVariantConsequenceData";
|
|
20
13
|
import { getSampleLabel } from "../utils/sample";
|
|
21
14
|
import { DecisionTree, Item, Sample } from "@molgenis/vip-report-api/src/Api";
|
|
22
15
|
import { Metadata, Record } from "@molgenis/vip-report-vcf/src/Vcf";
|
|
23
16
|
import { ValueArray } from "@molgenis/vip-report-vcf/src/ValueParser";
|
|
17
|
+
import { getSample, getVariant } from "./data/data";
|
|
24
18
|
|
|
25
|
-
export const SampleVariantConsequenceView: Component = () => {
|
|
26
|
-
const
|
|
27
|
-
|
|
19
|
+
export const SampleVariantConsequenceView: Component<RouteSectionProps> = (props) => {
|
|
20
|
+
const sample = createAsync(() => getSample(Number(props.params.sampleId)));
|
|
21
|
+
const variant = createAsync(() => getVariant(Number(props.params.variantId)));
|
|
22
|
+
const consequenceId = () => Number(props.params.consequenceId);
|
|
28
23
|
const [pedigreeSamples] = createResource(sample, fetchPedigreeSamples);
|
|
29
24
|
const [recordsMeta] = createResource(fetchRecordsMeta);
|
|
30
25
|
const [decisionTree] = createResource(fetchDecisionTree, { initialValue: null });
|
|
31
|
-
const [htsFileMeta] = createResource(fetchHtsFileMetadata);
|
|
32
26
|
|
|
33
27
|
return (
|
|
34
|
-
<Show when={sample()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
28
|
+
<Show when={sample()} fallback={<Loader />}>
|
|
29
|
+
{(sample) => (
|
|
30
|
+
<Show when={variant()} fallback={<Loader />}>
|
|
31
|
+
{(variant) => (
|
|
32
|
+
<>
|
|
33
|
+
<Breadcrumb
|
|
34
|
+
items={[
|
|
35
|
+
{ href: "/samples", text: "Samples" },
|
|
36
|
+
{ href: `/samples/${sample().id}`, text: getSampleLabel(sample().data) },
|
|
37
|
+
{ href: `/samples/${sample().id}/variants`, text: "Variants" },
|
|
38
|
+
{
|
|
39
|
+
href: `/samples/${sample().id}/variants/${variant().id}`,
|
|
40
|
+
text: getRecordLabel(variant()),
|
|
41
|
+
},
|
|
42
|
+
{ text: `Consequence #${consequenceId()}` },
|
|
43
|
+
]}
|
|
44
|
+
/>
|
|
45
|
+
<Show when={pedigreeSamples()} fallback={<Loader />}>
|
|
46
|
+
{(pedigreeSamples) => (
|
|
47
|
+
<Show when={recordsMeta()} fallback={<Loader />}>
|
|
48
|
+
{(recordsMeta) => (
|
|
49
|
+
<SampleVariantConsequence
|
|
50
|
+
sample={sample()}
|
|
51
|
+
pedigreeSamples={pedigreeSamples().items}
|
|
52
|
+
recordsMeta={recordsMeta()}
|
|
53
|
+
variant={variant()}
|
|
54
|
+
consequenceId={consequenceId()}
|
|
55
|
+
decisionTree={decisionTree()}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
</Show>
|
|
59
|
+
)}
|
|
60
|
+
</Show>
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
</Show>
|
|
64
|
+
)}
|
|
54
65
|
</Show>
|
|
55
66
|
);
|
|
56
67
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Component, createMemo, createResource, Show } from "solid-js";
|
|
2
|
-
import { useRouteData } from "@solidjs/router";
|
|
1
|
+
import { Component, createMemo, createResource, createSignal, onMount, Show } from "solid-js";
|
|
3
2
|
import {
|
|
4
3
|
HtsFileMetadata,
|
|
5
4
|
Item,
|
|
@@ -35,16 +34,24 @@ import { Breadcrumb } from "../components/Breadcrumb";
|
|
|
35
34
|
import { FieldMetadata } from "@molgenis/vip-report-vcf/src/MetadataParser";
|
|
36
35
|
import { FilterChangeEvent, FilterClearEvent, Filters } from "../components/filter/Filters";
|
|
37
36
|
import { DIRECTION_ASCENDING, DIRECTION_DESCENDING } from "../utils/sortUtils";
|
|
38
|
-
import { SampleRouteData } from "./data/SampleData";
|
|
39
37
|
import { useStore } from "../store";
|
|
40
38
|
import { Metadata } from "@molgenis/vip-report-vcf/src/Vcf";
|
|
41
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
getSampleAffectedStatusLabel,
|
|
41
|
+
getSampleFamilyMembersWithoutParents,
|
|
42
|
+
getSampleFather,
|
|
43
|
+
getSampleLabel,
|
|
44
|
+
getSampleMother,
|
|
45
|
+
getSampleSexLabel,
|
|
46
|
+
} from "../utils/sample";
|
|
42
47
|
import { arrayEquals } from "../utils/utils";
|
|
43
48
|
import { getAllelicBalanceQuery } from "../components/filter/FilterAllelicBalance";
|
|
44
49
|
import { RecordsPerPage, RecordsPerPageEvent } from "../components/RecordsPerPage";
|
|
50
|
+
import { createAsync, RouteSectionProps } from "@solidjs/router";
|
|
51
|
+
import { getSample } from "./data/data";
|
|
45
52
|
|
|
46
|
-
export const SampleVariantsView: Component = () => {
|
|
47
|
-
const
|
|
53
|
+
export const SampleVariantsView: Component<RouteSectionProps> = (props) => {
|
|
54
|
+
const sample = createAsync(() => getSample(Number(props.params.sampleId)));
|
|
48
55
|
|
|
49
56
|
const [pedigreeSamples] = createResource(sample, fetchPedigreeSamples);
|
|
50
57
|
const [samplePhenotypes] = createResource(sample, fetchPhenotypicFeatures);
|
|
@@ -56,7 +63,7 @@ export const SampleVariantsView: Component = () => {
|
|
|
56
63
|
<Breadcrumb
|
|
57
64
|
items={[
|
|
58
65
|
{ href: "/samples", text: "Samples" },
|
|
59
|
-
{ href: `/samples/${sample()!.id}`, text: getSampleLabel(sample()
|
|
66
|
+
{ href: `/samples/${sample()!.id}`, text: getSampleLabel(sample()!.data) },
|
|
60
67
|
{ text: "Variants" },
|
|
61
68
|
]}
|
|
62
69
|
/>
|
|
@@ -82,6 +89,20 @@ export const SampleVariants: Component<{
|
|
|
82
89
|
}> = (props) => {
|
|
83
90
|
const [state, actions] = useStore();
|
|
84
91
|
|
|
92
|
+
const samples = createMemo(() => [props.sample.data, ...props.pedigreeSamples.map((item) => item.data)]);
|
|
93
|
+
|
|
94
|
+
const [proband, setProband] = createSignal<Sample | undefined>();
|
|
95
|
+
const [father, setFather] = createSignal<Sample | undefined>();
|
|
96
|
+
const [mother, setMother] = createSignal<Sample | undefined>();
|
|
97
|
+
const [otherFamilyMembers, setOtherFamilyMembers] = createSignal<Sample[]>([]);
|
|
98
|
+
|
|
99
|
+
onMount(() => {
|
|
100
|
+
setProband(props.sample.data);
|
|
101
|
+
setMother(getSampleMother(proband() as Sample, samples()));
|
|
102
|
+
setFather(getSampleFather(proband() as Sample, samples()));
|
|
103
|
+
setOtherFamilyMembers(getSampleFamilyMembersWithoutParents(proband() as Sample, samples()));
|
|
104
|
+
});
|
|
105
|
+
|
|
85
106
|
function getStateVariants() {
|
|
86
107
|
return state.sampleVariants ? state.sampleVariants[props.sample.id]?.variants : undefined;
|
|
87
108
|
}
|
|
@@ -311,9 +332,51 @@ export const SampleVariants: Component<{
|
|
|
311
332
|
},
|
|
312
333
|
]);
|
|
313
334
|
|
|
335
|
+
function getTitleSampleSexLabel(sample: Sample): string {
|
|
336
|
+
const label = getSampleSexLabel(sample);
|
|
337
|
+
return label !== "?" ? label : "sex:?";
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getTitleAffectedStatusLabel(sample: Sample): string {
|
|
341
|
+
const label = getSampleAffectedStatusLabel(sample);
|
|
342
|
+
return label !== "?" ? label : "affected status:?";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const title = (): string => {
|
|
346
|
+
return `Reported variants for ${getSampleLabel(props.sample.data)} (${getTitleSampleSexLabel(props.sample.data)} ${getTitleAffectedStatusLabel(props.sample.data)})`;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const subtitle = (): string | undefined => {
|
|
350
|
+
const sampleFather = father();
|
|
351
|
+
const sampleMother = mother();
|
|
352
|
+
const sampleOtherFamilyMembers = otherFamilyMembers();
|
|
353
|
+
|
|
354
|
+
if (sampleFather === undefined && sampleMother === undefined && sampleOtherFamilyMembers.length === 0) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const tokens: string[] = [];
|
|
359
|
+
if (sampleMother !== undefined) {
|
|
360
|
+
tokens.push(`mother (${getTitleAffectedStatusLabel(sampleMother)})`);
|
|
361
|
+
}
|
|
362
|
+
if (sampleFather !== undefined) {
|
|
363
|
+
tokens.push(`father (${getTitleAffectedStatusLabel(sampleFather)})`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
for (const familyMember of sampleOtherFamilyMembers) {
|
|
367
|
+
tokens.push(
|
|
368
|
+
`${getSampleLabel(familyMember)} (${getTitleSampleSexLabel(familyMember)} ${getTitleAffectedStatusLabel(familyMember)})`,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
let str = tokens.pop() as string;
|
|
373
|
+
if (tokens.length > 0) str = `${tokens.join(", ")} and ${str}`;
|
|
374
|
+
return `Includes genotypes for ${str}`;
|
|
375
|
+
};
|
|
376
|
+
|
|
314
377
|
return (
|
|
315
|
-
<div class="columns is-variable is-1">
|
|
316
|
-
<div class="column is-1-fullhd is-2">
|
|
378
|
+
<div class="variants columns is-variable is-1">
|
|
379
|
+
<div class="scrolling-div column is-1-fullhd is-2">
|
|
317
380
|
<SearchBox value={searchQuery()} onInput={onSearchChange} />
|
|
318
381
|
<Filters
|
|
319
382
|
fields={filterInfoFields()}
|
|
@@ -323,7 +386,15 @@ export const SampleVariants: Component<{
|
|
|
323
386
|
onClear={onFilterClear}
|
|
324
387
|
/>
|
|
325
388
|
</div>
|
|
326
|
-
<div class="column">
|
|
389
|
+
<div class="scrolling-div column">
|
|
390
|
+
<div class="columns is-gapless">
|
|
391
|
+
<div class="column">
|
|
392
|
+
<p class="title is-3">{title()}</p>
|
|
393
|
+
<Show when={subtitle()} keyed>
|
|
394
|
+
{(subtitle) => <p class="subtitle is-5">{subtitle}</p>}
|
|
395
|
+
</Show>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
327
398
|
<div class="columns is-gapless">
|
|
328
399
|
<div class="column is-offset-1-fullhd is-3-fullhd is-4">
|
|
329
400
|
<Show when={records()} fallback={<Loader />} keyed>
|
package/src/views/Samples.tsx
CHANGED
|
@@ -75,42 +75,44 @@ export const Samples: Component = () => {
|
|
|
75
75
|
return (
|
|
76
76
|
<>
|
|
77
77
|
<Breadcrumb items={[{ text: "Samples" }]} />
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
<div class="columns is-centered">
|
|
79
|
+
<div class="column is-three-quarters-widescreen">
|
|
80
|
+
<p class="title is-2">Samples</p>
|
|
81
|
+
<p class="subtitle is-4">Explore samples and sample variants</p>
|
|
82
|
+
<Show when={samples()} fallback={<Loader />} keyed>
|
|
83
|
+
{(samples) => (
|
|
84
|
+
<>
|
|
85
|
+
<div class="columns">
|
|
86
|
+
<div class="column is-4 is-offset-3">{<Pager page={samples.page} onPageChange={onPageChange} />}</div>
|
|
87
|
+
<div class="column is-2 is-offset-1">
|
|
88
|
+
{<span class="is-pulled-right">{samples.page.totalElements} records</span>}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
<div class="columns">
|
|
93
|
+
<div class="column is-2-widescreen is-3">
|
|
94
|
+
<SearchBox onInput={onSearchChange} value={state.samples?.searchQuery} />
|
|
95
|
+
<p class="has-text-weight-semibold">Proband</p>
|
|
96
|
+
<div class="field">
|
|
97
|
+
<div class="control">
|
|
98
|
+
<Checkbox
|
|
99
|
+
value={"proband"}
|
|
100
|
+
label=""
|
|
101
|
+
onChange={onProbandFilterChange}
|
|
102
|
+
checked={state.samples?.probandFilterValue}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="column">
|
|
108
|
+
{!phenotypes.loading && <SampleTable samples={samples.items} phenotypes={phenotypes().items} />}
|
|
100
109
|
</div>
|
|
101
110
|
</div>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</span>
|
|
108
|
-
{!phenotypes.loading && <SampleTable samples={samples.items} phenotypes={phenotypes().items} />}
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
</>
|
|
112
|
-
)}
|
|
113
|
-
</Show>
|
|
111
|
+
</>
|
|
112
|
+
)}
|
|
113
|
+
</Show>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
114
116
|
</>
|
|
115
117
|
);
|
|
116
118
|
};
|