@molgenis/vip-report-template 6.2.0 → 7.0.0

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.
Files changed (285) hide show
  1. package/.nvmrc +1 -1
  2. package/.travis.yml +8 -8
  3. package/README.md +411 -1
  4. package/eslint.config.mjs +11 -0
  5. package/package.json +40 -35
  6. package/scripts/deploy_npm_registry.sh +5 -0
  7. package/src/App.tsx +30 -24
  8. package/src/assets/sass/main.scss +12 -4
  9. package/src/components/Allele.tsx +95 -0
  10. package/src/components/Anchor.tsx +1 -1
  11. package/src/components/Breadcrumb.tsx +6 -4
  12. package/src/components/DatasetDropdown.tsx +12 -25
  13. package/src/components/ErrorNotification.tsx +9 -0
  14. package/src/components/GenomeBrowser.tsx +40 -23
  15. package/src/components/HpoTerm.tsx +1 -1
  16. package/src/components/{record/Pager.tsx → Pager.tsx} +21 -14
  17. package/src/components/RecordsPerPage.tsx +9 -7
  18. package/src/components/RecordsTable.tsx +130 -0
  19. package/src/components/SampleTable.tsx +70 -98
  20. package/src/components/SearchBox.tsx +8 -2
  21. package/src/components/Sort.tsx +28 -25
  22. package/src/components/Table.tsx +16 -0
  23. package/src/components/Tooltip.tsx +20 -0
  24. package/src/components/VariantBreadcrumb.tsx +54 -0
  25. package/src/components/VariantConsequenceContainer.tsx +100 -0
  26. package/src/components/VariantConsequenceTable.tsx +58 -0
  27. package/src/components/VariantContainer.tsx +71 -0
  28. package/src/components/VariantFilters.tsx +27 -0
  29. package/src/components/VariantGenotypeTable.tsx +44 -0
  30. package/src/components/VariantInfoTable.tsx +24 -33
  31. package/src/components/VariantResults.tsx +103 -0
  32. package/src/components/VariantTable.tsx +62 -66
  33. package/src/components/VariantTypeSelect.tsx +34 -0
  34. package/src/components/VariantsContainer.tsx +150 -0
  35. package/src/components/VariantsContainerHeader.tsx +70 -0
  36. package/src/components/field/Field.tsx +80 -0
  37. package/src/components/field/FieldAlt.tsx +19 -0
  38. package/src/components/field/FieldChrom.tsx +6 -0
  39. package/src/components/{record/Id.tsx → field/FieldFilter.tsx} +2 -1
  40. package/src/components/field/FieldFormat.tsx +10 -0
  41. package/src/components/{record/Filter.tsx → field/FieldId.tsx} +1 -1
  42. package/src/components/field/FieldPos.tsx +6 -0
  43. package/src/components/field/FieldQual.tsx +6 -0
  44. package/src/components/field/FieldRef.tsx +8 -0
  45. package/src/components/field/composed/FieldClinVar.tsx +72 -0
  46. package/src/components/field/composed/FieldComposed.tsx +68 -0
  47. package/src/components/field/composed/FieldGene.tsx +39 -0
  48. package/src/components/field/composed/FieldGenotype.tsx +35 -0
  49. package/src/components/{record/format/GenotypeField.tsx → field/composed/FieldGenotypeSnvSv.tsx} +20 -16
  50. package/src/components/field/composed/FieldGenotypeStr.tsx +31 -0
  51. package/src/components/field/composed/FieldGnomAd.tsx +58 -0
  52. package/src/components/field/composed/FieldHpo.tsx +50 -0
  53. package/src/components/field/composed/FieldInheritanceModes.tsx +32 -0
  54. package/src/components/field/composed/FieldLocus.tsx +18 -0
  55. package/src/components/field/composed/FieldVipC.tsx +25 -0
  56. package/src/components/field/composed/FieldVipCS.tsx +15 -0
  57. package/src/components/field/composed/FieldVkgl.tsx +37 -0
  58. package/src/components/field/genotype/FieldGenotype.tsx +19 -0
  59. package/src/components/field/genotype/FieldGenotypeType.tsx +9 -0
  60. package/src/components/field/info/FieldConsequence.tsx +15 -0
  61. package/src/components/{record/info/Hgvs.tsx → field/info/FieldHgvs.tsx} +4 -6
  62. package/src/components/field/info/FieldInfo.tsx +27 -0
  63. package/src/components/{record/info/PubMed.tsx → field/info/FieldPubMed.tsx} +4 -7
  64. package/src/components/field/typed/FieldCategorical.tsx +17 -0
  65. package/src/components/{record/field/FieldValueCharacter.tsx → field/typed/FieldCharacter.tsx} +3 -2
  66. package/src/components/{record/field/FieldValueFlag.tsx → field/typed/FieldFlag.tsx} +3 -2
  67. package/src/components/{record/field/FieldValueFloat.tsx → field/typed/FieldFloat.tsx} +3 -2
  68. package/src/components/{record/field/FieldValueInteger.tsx → field/typed/FieldInteger.tsx} +3 -2
  69. package/src/components/{record/field/FieldValueString.tsx → field/typed/FieldString.tsx} +3 -2
  70. package/src/components/field/typed/FieldTyped.tsx +20 -0
  71. package/src/components/field/typed/FieldTypedItem.tsx +49 -0
  72. package/src/components/field/typed/FieldTypedMultiple.tsx +21 -0
  73. package/src/components/filter/Filter.tsx +56 -48
  74. package/src/components/filter/FilterWrapper.scss +23 -0
  75. package/src/components/filter/FilterWrapper.tsx +63 -0
  76. package/src/components/filter/composed/FilterAllelicImbalance.tsx +26 -0
  77. package/src/components/filter/composed/FilterComposed.tsx +92 -0
  78. package/src/components/filter/composed/FilterDeNovo.tsx +35 -0
  79. package/src/components/filter/composed/FilterHpo.tsx +16 -0
  80. package/src/components/filter/composed/FilterInheritance.tsx +42 -0
  81. package/src/components/filter/composed/FilterLocus.tsx +75 -0
  82. package/src/components/filter/composed/FilterVipC.tsx +16 -0
  83. package/src/components/filter/composed/FilterVipCS.tsx +16 -0
  84. package/src/components/filter/fixed/FilterAlt.tsx +20 -0
  85. package/src/components/filter/fixed/FilterChrom.tsx +22 -0
  86. package/src/components/filter/fixed/FilterFilter.tsx +20 -0
  87. package/src/components/filter/fixed/FilterFixed.tsx +96 -0
  88. package/src/components/filter/fixed/FilterId.tsx +20 -0
  89. package/src/components/filter/fixed/FilterPos.tsx +22 -0
  90. package/src/components/filter/fixed/FilterQual.tsx +21 -0
  91. package/src/components/filter/fixed/FilterRef.tsx +22 -0
  92. package/src/components/filter/typed/FilterCategorical.tsx +119 -0
  93. package/src/components/filter/typed/FilterFlag.tsx +23 -0
  94. package/src/components/filter/typed/FilterInterval.tsx +72 -0
  95. package/src/components/filter/typed/FilterString.tsx +43 -0
  96. package/src/components/filter/typed/FilterTyped.tsx +56 -0
  97. package/src/components/form/ButtonApply.tsx +11 -0
  98. package/src/components/form/ButtonDownload.tsx +11 -0
  99. package/src/components/form/ButtonReset.tsx +9 -0
  100. package/src/components/{Checkbox.tsx → form/Checkbox.tsx} +4 -9
  101. package/src/components/form/Input.tsx +19 -0
  102. package/src/components/form/Select.scss +7 -0
  103. package/src/components/form/Select.tsx +34 -0
  104. package/src/components/tree/DecisionTreeBoolMultiQuery.tsx +1 -1
  105. package/src/components/tree/DecisionTreeBoolQuery.tsx +1 -1
  106. package/src/components/tree/DecisionTreeNode.tsx +41 -39
  107. package/src/components/tree/DecisionTreeNodeBool.tsx +1 -1
  108. package/src/components/tree/DecisionTreeNodeBoolMulti.tsx +1 -1
  109. package/src/components/tree/DecisionTreeNodeCategorical.tsx +1 -1
  110. package/src/components/tree/DecisionTreeNodeExists.tsx +1 -1
  111. package/src/components/tree/DecisionTreeNodeLeaf.tsx +1 -1
  112. package/src/components/tree/DecisionTreeOutcomeNode.tsx +1 -1
  113. package/src/components/tree/DecisionTreePath.tsx +1 -1
  114. package/src/igv.d.ts +2 -1
  115. package/src/index.tsx +48 -19
  116. package/src/mocks/GRCh37/decisionTree.json +23 -22
  117. package/src/mocks/GRCh37/field_metadata.json +435 -95
  118. package/src/mocks/GRCh37/sampleTree.json +21 -1
  119. package/src/mocks/GRCh37/static.ts +62 -134
  120. package/src/mocks/GRCh37/vcf/family.vcf.blob +9 -3
  121. package/src/mocks/GRCh38/decisionTree.json +52 -33
  122. package/src/mocks/GRCh38/decisionTreeStr.json +572 -0
  123. package/src/mocks/GRCh38/fasta/chr1_149380406-149403321.fasta.gz.blob +0 -0
  124. package/src/mocks/GRCh38/field_metadata.json +435 -95
  125. package/src/mocks/GRCh38/sampleTree.json +175 -0
  126. package/src/mocks/GRCh38/static.ts +101 -42
  127. package/src/mocks/GRCh38/str.cram.blob +0 -0
  128. package/src/mocks/GRCh38/str.cram.crai.blob +0 -0
  129. package/src/mocks/GRCh38/vcf/family.vcf.blob +25 -24
  130. package/src/mocks/GRCh38/vcf/no_vep.vcf.blob +29 -28
  131. package/src/mocks/GRCh38/vcf/samples_0.vcf.blob +28 -27
  132. package/src/mocks/GRCh38/vcf/samples_1.vcf.blob +29 -28
  133. package/src/mocks/GRCh38/vcf/samples_100.vcf.blob +28 -27
  134. package/src/mocks/GRCh38/vcf/str.vcf.blob +321 -0
  135. package/src/mocks/MockApiClient.ts +339 -332
  136. package/src/mocks/config_cram.json +701 -0
  137. package/src/mocks/config_vcf.json +699 -0
  138. package/src/store/app.ts +30 -0
  139. package/src/store/index.tsx +3 -168
  140. package/src/store/variants.ts +182 -0
  141. package/src/types/config.d.ts +190 -0
  142. package/src/types/configCellComposed.d.ts +86 -0
  143. package/src/types/configCells.d.ts +129 -0
  144. package/src/types/configFilter.d.ts +80 -0
  145. package/src/types/configFilterComposed.d.ts +60 -0
  146. package/src/types/configSort.d.ts +13 -0
  147. package/src/types/filter.d.ts +17 -0
  148. package/src/types/store.d.ts +34 -0
  149. package/src/utils/api.ts +281 -0
  150. package/src/utils/config/config.ts +182 -0
  151. package/src/utils/config/configCells.ts +74 -0
  152. package/src/utils/config/configCellsComposed.ts +508 -0
  153. package/src/utils/config/configCellsField.ts +61 -0
  154. package/src/utils/config/configCellsFixed.ts +126 -0
  155. package/src/utils/config/configFilters.ts +46 -0
  156. package/src/utils/config/configFiltersComposed.ts +208 -0
  157. package/src/utils/config/configFiltersField.ts +49 -0
  158. package/src/utils/config/configFiltersFixed.ts +106 -0
  159. package/src/utils/config/configSorts.ts +44 -0
  160. package/src/utils/config/configValidator.ts +380 -0
  161. package/src/utils/config/configVip.ts +25 -0
  162. package/src/utils/csq.ts +115 -0
  163. package/src/utils/decisionTree.ts +45 -0
  164. package/src/utils/download.ts +30 -0
  165. package/src/utils/error.ts +69 -0
  166. package/src/utils/query/query.ts +55 -0
  167. package/src/utils/query/queryFilter.ts +132 -0
  168. package/src/utils/query/queryFilterComposed.ts +247 -0
  169. package/src/utils/query/queryFilterField.ts +75 -0
  170. package/src/utils/query/queryFilterFixed.ts +44 -0
  171. package/src/utils/query/querySample.ts +18 -0
  172. package/src/utils/query/queryVariantType.ts +76 -0
  173. package/src/utils/query/selector.ts +41 -0
  174. package/src/utils/{sortUtils.ts → query/sort.ts} +32 -11
  175. package/src/utils/sample.ts +19 -35
  176. package/src/utils/utils.ts +66 -2
  177. package/src/utils/variantType.ts +43 -0
  178. package/src/utils/vcf.ts +352 -0
  179. package/src/views/Help.tsx +109 -114
  180. package/src/views/Home.tsx +3 -2
  181. package/src/views/Sample.tsx +12 -7
  182. package/src/views/SampleVariant.tsx +23 -112
  183. package/src/views/SampleVariantConsequence.tsx +54 -144
  184. package/src/views/SampleVariants.tsx +33 -445
  185. package/src/views/SampleVariantsRedirect.tsx +20 -0
  186. package/src/views/Samples.tsx +7 -10
  187. package/src/views/Variant.tsx +31 -61
  188. package/src/views/VariantConsequence.tsx +42 -72
  189. package/src/views/Variants.tsx +29 -138
  190. package/src/views/VariantsRedirect.tsx +25 -0
  191. package/src/views/data/data.tsx +32 -6
  192. package/tests/store/variants.test.ts +122 -0
  193. package/tests/utils/config/config.test.ts +167 -0
  194. package/tests/utils/config/configCells.test.ts +86 -0
  195. package/tests/utils/config/configCellsComposed.test.ts +1163 -0
  196. package/tests/utils/config/configCellsField.test.ts +164 -0
  197. package/tests/utils/config/configCellsFixed.test.ts +99 -0
  198. package/tests/utils/config/configFilters.test.ts +80 -0
  199. package/tests/utils/config/configFiltersComposed.test.ts +504 -0
  200. package/tests/utils/config/configFiltersField.test.ts +140 -0
  201. package/tests/utils/config/configFiltersFixed.test.ts +81 -0
  202. package/tests/utils/config/configSorts.test.ts +55 -0
  203. package/tests/utils/config/configValidator.test.ts +56 -0
  204. package/tests/utils/config/configVip.test.ts +53 -0
  205. package/tests/utils/decisionTree.test.ts +71 -0
  206. package/tests/utils/download.test.ts +20 -0
  207. package/tests/utils/query/query.test.ts +84 -0
  208. package/tests/utils/query/queryFilter.test.ts +243 -0
  209. package/tests/utils/query/queryFilterComposed.test.ts +301 -0
  210. package/tests/utils/query/queryFilterField.test.ts +75 -0
  211. package/tests/utils/query/queryFilterFixed.test.ts +86 -0
  212. package/tests/utils/query/querySample.test.ts +45 -0
  213. package/tests/utils/query/queryVariantType.test.ts +56 -0
  214. package/{src/__tests__/sortUtils.test.ts → tests/utils/query/sort.test.ts} +3 -4
  215. package/tests/utils/sample.test.ts +259 -0
  216. package/tests/utils/utils.test.ts +120 -0
  217. package/tests/utils/variantType.test.ts +48 -0
  218. package/tests/utils/vcf.test.ts +649 -0
  219. package/tsconfig.json +6 -2
  220. package/vite.config.mts +20 -3
  221. package/.eslintignore +0 -4
  222. package/.eslintrc.js +0 -23
  223. package/src/Api.ts +0 -12
  224. package/src/__tests__/decisionTreeUtils.test.ts +0 -75
  225. package/src/__tests__/field.test.ts +0 -107
  226. package/src/__tests__/query.test.ts +0 -188
  227. package/src/__tests__/sample.test.ts +0 -184
  228. package/src/__tests__/utils.test.ts +0 -24
  229. package/src/__tests__/viewUtils.test.ts +0 -125
  230. package/src/components/ConsequenceTable.tsx +0 -45
  231. package/src/components/Error.tsx +0 -9
  232. package/src/components/FieldHeader.tsx +0 -26
  233. package/src/components/InfoCollapsablePane.tsx +0 -90
  234. package/src/components/VariantInfoNestedTable.tsx +0 -127
  235. package/src/components/VariantSampleTable.tsx +0 -58
  236. package/src/components/VariantsSampleTable.tsx +0 -184
  237. package/src/components/VariantsTable.tsx +0 -125
  238. package/src/components/filter/FilterAllelicBalance.tsx +0 -81
  239. package/src/components/filter/FilterCategorical.tsx +0 -81
  240. package/src/components/filter/FilterClinVar.tsx +0 -21
  241. package/src/components/filter/FilterGene.tsx +0 -34
  242. package/src/components/filter/FilterHpo.tsx +0 -161
  243. package/src/components/filter/FilterInheritance.tsx +0 -162
  244. package/src/components/filter/FilterIntegerGq.tsx +0 -47
  245. package/src/components/filter/FilterVI.tsx +0 -68
  246. package/src/components/filter/FilterVariantType.tsx +0 -146
  247. package/src/components/filter/Filters.tsx +0 -29
  248. package/src/components/filter/InfoFilter.tsx +0 -39
  249. package/src/components/filter/InfoFilters.tsx +0 -35
  250. package/src/components/filter/SampleFilters.tsx +0 -93
  251. package/src/components/filter/SamplesFilters.tsx +0 -33
  252. package/src/components/record/Allele.tsx +0 -38
  253. package/src/components/record/AlleleBreakend.tsx +0 -5
  254. package/src/components/record/AlleleMissing.tsx +0 -5
  255. package/src/components/record/AlleleNucs.tsx +0 -49
  256. package/src/components/record/AlleleSymbolic.tsx +0 -5
  257. package/src/components/record/Alt.tsx +0 -17
  258. package/src/components/record/Chrom.tsx +0 -5
  259. package/src/components/record/Format.tsx +0 -40
  260. package/src/components/record/Info.tsx +0 -55
  261. package/src/components/record/Pos.tsx +0 -5
  262. package/src/components/record/Qual.tsx +0 -5
  263. package/src/components/record/RecordDownload.tsx +0 -66
  264. package/src/components/record/Ref.tsx +0 -6
  265. package/src/components/record/field/Field.tsx +0 -36
  266. package/src/components/record/field/FieldMultipleValue.tsx +0 -22
  267. package/src/components/record/field/FieldSingleValue.tsx +0 -35
  268. package/src/components/record/info/ClinVar.tsx +0 -81
  269. package/src/components/record/info/Consequence.tsx +0 -18
  270. package/src/components/record/info/Gene.tsx +0 -56
  271. package/src/components/record/info/GnomAD.tsx +0 -54
  272. package/src/components/record/info/Hpo.tsx +0 -52
  273. package/src/components/record/info/InheritanceModes.tsx +0 -22
  274. package/src/components/record/info/VipC.tsx +0 -23
  275. package/src/components/record/info/Vkgl.tsx +0 -42
  276. package/src/mocks/GRCh37/vcf/no_vep.vcf.blob +0 -61
  277. package/src/mocks/GRCh37/vcf/samples_0.vcf.blob +0 -93
  278. package/src/mocks/GRCh37/vcf/samples_1.vcf.blob +0 -93
  279. package/src/mocks/GRCh37/vcf/samples_100.vcf.blob +0 -93
  280. package/src/utils/ApiUtils.ts +0 -263
  281. package/src/utils/csqUtils.ts +0 -27
  282. package/src/utils/decisionTreeUtils.ts +0 -31
  283. package/src/utils/field.ts +0 -49
  284. package/src/utils/query.ts +0 -154
  285. package/src/utils/viewUtils.ts +0 -32
@@ -1,455 +1,43 @@
1
- import { Component, createMemo, createResource, createSignal, onMount, Show } from "solid-js";
2
- import {
3
- HtsFileMetadata,
4
- Item,
5
- Params,
6
- PhenotypicFeature,
7
- QueryClause,
8
- Sample,
9
- SortPath,
10
- } from "@molgenis/vip-report-api/src/Api";
1
+ import { Component, Show } from "solid-js";
11
2
  import { Loader } from "../components/Loader";
12
- import { SearchBox } from "../components/SearchBox";
13
- import { Sort, SortEvent } from "../components/Sort";
14
- import { Pager } from "../components/record/Pager";
15
- import { RecordDownload } from "../components/record/RecordDownload";
16
- import {
17
- createSampleQuery,
18
- infoSelector,
19
- infoSortPath,
20
- sampleCustomKey,
21
- sampleSelector,
22
- selector,
23
- selectorKey,
24
- } from "../utils/query";
25
- import { VariantsSampleTable } from "../components/VariantsSampleTable";
26
- import {
27
- fetchHtsFileMetadata,
28
- fetchPedigreeSamples,
29
- fetchPhenotypicFeatures,
30
- fetchRecords,
31
- fetchRecordsMeta,
32
- } from "../utils/ApiUtils";
33
- import { Breadcrumb } from "../components/Breadcrumb";
34
- import { FieldMetadata } from "@molgenis/vip-report-vcf/src/MetadataParser";
35
- import { FilterChangeEvent, FilterClearEvent, Filters } from "../components/filter/Filters";
36
- import { DIRECTION_ASCENDING, DIRECTION_DESCENDING } from "../utils/sortUtils";
37
- import { useStore } from "../store";
38
- import { Metadata } from "@molgenis/vip-report-vcf/src/Vcf";
39
- import {
40
- getSampleAffectedStatusLabel,
41
- getSampleFamilyMembersWithoutParents,
42
- getSampleFather,
43
- getSampleLabel,
44
- getSampleMother,
45
- getSampleSexLabel,
46
- } from "../utils/sample";
47
- import { arrayEquals } from "../utils/utils";
48
- import { getAllelicBalanceQuery } from "../components/filter/FilterAllelicBalance";
49
- import { RecordsPerPage, RecordsPerPageEvent } from "../components/RecordsPerPage";
50
3
  import { createAsync, RouteSectionProps } from "@solidjs/router";
51
- import { getSample } from "./data/data";
4
+ import { getConfig, getMetadata, getSampleById } from "./data/data";
5
+ import { parseVariantType } from "../utils/variantType.ts";
6
+ import { VariantsContainer } from "../components/VariantsContainer";
7
+ import { VariantBreadcrumb } from "../components/VariantBreadcrumb.tsx";
8
+ import { useStore } from "../store";
9
+ import { wrapStore } from "../store/variants.ts";
52
10
 
53
- export const SampleVariantsView: Component<RouteSectionProps> = (props) => {
54
- const sample = createAsync(() => getSample(Number(props.params.sampleId)));
11
+ export const SampleVariants: Component<RouteSectionProps> = (props) => {
12
+ const store = useStore();
55
13
 
56
- const [pedigreeSamples] = createResource(sample, fetchPedigreeSamples);
57
- const [samplePhenotypes] = createResource(sample, fetchPhenotypicFeatures);
58
- const [recordsMeta] = createResource(fetchRecordsMeta);
59
- const [htsFileMeta] = createResource(fetchHtsFileMetadata);
14
+ const variantType = () => parseVariantType(props.params.variantType);
15
+ const config = createAsync(() => getConfig());
16
+ const metadata = createAsync(() => getMetadata());
17
+ const sample = createAsync(() => getSampleById(props.params.sampleId));
60
18
 
61
19
  return (
62
20
  <Show when={sample()} fallback={<Loader />}>
63
- <Breadcrumb
64
- items={[
65
- { href: "/samples", text: "Samples" },
66
- { href: `/samples/${sample()!.id}`, text: getSampleLabel(sample()!.data) },
67
- { text: "Variants" },
68
- ]}
69
- />
70
- <Show when={pedigreeSamples() && samplePhenotypes() && recordsMeta() && htsFileMeta()} fallback={<Loader />}>
71
- <SampleVariants
72
- sample={sample()!}
73
- samplePhenotypes={samplePhenotypes()!}
74
- pedigreeSamples={pedigreeSamples()!.items}
75
- recordsMeta={recordsMeta()!}
76
- htsFileMeta={htsFileMeta()!}
77
- />
78
- </Show>
79
- </Show>
80
- );
81
- };
82
-
83
- export const SampleVariants: Component<{
84
- sample: Item<Sample>;
85
- samplePhenotypes: PhenotypicFeature[];
86
- pedigreeSamples: Item<Sample>[];
87
- recordsMeta: Metadata;
88
- htsFileMeta: HtsFileMetadata;
89
- }> = (props) => {
90
- const [state, actions] = useStore();
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
-
106
- function getStateVariants() {
107
- return state.sampleVariants ? state.sampleVariants[props.sample.id]?.variants : undefined;
108
- }
109
-
110
- // state initialization - start
111
- if (getStateVariants()?.page === undefined) {
112
- actions.setSampleVariantsPage(props.sample, 0);
113
- }
114
- if (getStateVariants()?.pageSize === undefined) {
115
- actions.setSampleVariantsPageSize(props.sample, 20);
116
- }
117
-
118
- if (getStateVariants()?.filterQueries === undefined) {
119
- const hpoField = props.recordsMeta.info?.CSQ?.nested?.items?.find((field) => field.id === "HPO");
120
- const gadoField = props.recordsMeta.info?.CSQ?.nested?.items?.find((field) => field.id === "GADO_PD");
121
- if (hpoField && props.samplePhenotypes.length > 0) {
122
- const selectorHpo = infoSelector(hpoField);
123
- const queries: QueryClause[] = [
124
- {
125
- selector: selectorHpo,
126
- operator: "any_has_any",
127
- args: props.samplePhenotypes.map((phenotype) => phenotype.type.id),
128
- },
129
- ];
130
- if (gadoField) {
131
- const selectorGado = infoSelector(gadoField);
132
- queries.push({
133
- selector: selectorGado,
134
- operator: "has_any",
135
- args: ["HC"],
136
- });
137
- }
138
- actions.setSampleVariantsFilterQuery(
139
- props.sample,
140
- {
141
- operator: "or",
142
- args: queries,
143
- },
144
- selectorKey(selectorHpo),
145
- );
146
- }
147
-
148
- const vimField = props.recordsMeta.format?.VIM;
149
- if (vimField) {
150
- const selectorVim = sampleSelector(props.sample, vimField);
151
- actions.setSampleVariantsFilterQuery(
152
- props.sample,
153
- {
154
- operator: "or",
155
- args: [
156
- {
157
- selector: selectorVim,
158
- operator: "==",
159
- args: 1,
160
- },
161
- {
162
- selector: selectorVim,
163
- operator: "==",
164
- args: null,
165
- },
166
- {
167
- selector: selectorVim,
168
- operator: "==",
169
- args: undefined,
170
- },
171
- ],
172
- },
173
- sampleCustomKey(props.sample, "VIP_Inheritance"),
174
- );
175
- }
176
- const gqField = props.recordsMeta.format?.GQ;
177
- if (gqField) {
178
- const selectorGq = sampleSelector(props.sample, gqField);
179
- actions.setSampleVariantsFilterQuery(
180
- props.sample,
181
- {
182
- operator: "or",
183
- args: [
184
- {
185
- selector: selectorGq,
186
- operator: ">=",
187
- args: 20,
188
- },
189
- {
190
- selector: selectorGq,
191
- operator: "==",
192
- args: null,
193
- },
194
- {
195
- selector: selectorGq,
196
- operator: "==",
197
- args: undefined,
198
- },
199
- ],
200
- },
201
- selectorKey(selectorGq),
202
- );
203
- }
204
- const viabField = props.recordsMeta.format?.VIAB;
205
- if (viabField) {
206
- actions.setSampleVariantsFilterQuery(
207
- props.sample,
208
- getAllelicBalanceQuery(props.sample.data.index),
209
- selectorKey(["s", props.sample.data.index, ...selector(viabField)]),
210
- );
211
- }
212
- const viField = props.recordsMeta.format?.VI;
213
- if (viField) {
214
- const selectorVi = sampleSelector(props.sample, viField);
215
- actions.setSampleVariantsFilterQuery(
216
- props.sample,
217
- {
218
- selector: selectorVi,
219
- operator: "has_any",
220
- args: ["AD", "AR", "AR_C", "XLD", "XLR", "YL", "MT"],
221
- },
222
- selectorKey(selectorVi),
223
- );
224
- }
225
- }
226
-
227
- if (getStateVariants()?.sort === undefined) {
228
- const capiceScField = props.recordsMeta.info?.CSQ?.nested?.items?.find((field) => field.id === "CAPICE_SC");
229
- if (capiceScField) {
230
- actions.setSampleVariantsSort(props.sample, {
231
- property: infoSortPath(capiceScField),
232
- compare: DIRECTION_DESCENDING,
233
- });
234
- }
235
- }
236
- // state initialization - end
237
-
238
- const infoFields = createMemo(() => {
239
- const csqNestedFields = props.recordsMeta.info.CSQ?.nested?.items;
240
- const includedFields = [
241
- "Consequence",
242
- "SYMBOL",
243
- "InheritanceModesGene",
244
- "HPO",
245
- "HGVSc",
246
- "HGVSp",
247
- "CAPICE_SC",
248
- "VIPC",
249
- "UMCG_CL",
250
- "VKGL_CL",
251
- "clinVar_CLNSIG",
252
- "gnomAD_AF",
253
- "gnomAD_HN",
254
- "PUBMED",
255
- ];
256
- return csqNestedFields
257
- ? (includedFields
258
- .map((fieldId) => csqNestedFields.find((field) => field.id === fieldId))
259
- .filter((field) => field !== undefined) as FieldMetadata[])
260
- : [];
261
- });
262
-
263
- const formatFields = createMemo(() => {
264
- const formatFieldMap = props.recordsMeta.format;
265
- const includedFields = ["VIM", "VID", "VI", "GQ", "VIAB", "GT", "DP"];
266
- return formatFieldMap
267
- ? includedFields.map((fieldId) => formatFieldMap[fieldId]).filter((field) => field !== undefined)
268
- : [];
269
- });
270
-
271
- const filterInfoFields = createMemo(() => {
272
- const csqNestedFields = props.recordsMeta.info.CSQ?.nested?.items;
273
- const includedFields = [];
274
- const additionalCsqFieldsIds = ["IncompletePenetrance"];
275
- const filterInfoFieldsIds = ["SVTYPE"];
276
- const additionalCsqFields = csqNestedFields
277
- ? (additionalCsqFieldsIds
278
- .map((fieldId) => csqNestedFields.find((field) => field.id === fieldId))
279
- .filter((field) => field !== undefined) as FieldMetadata[])
280
- : [];
281
- const filterInfoFields = filterInfoFieldsIds
282
- .map((fieldId) => props.recordsMeta.info[fieldId])
283
- .filter((field) => field !== undefined);
284
- includedFields.push(...infoFields());
285
- includedFields.push(...additionalCsqFields);
286
- includedFields.push(...filterInfoFields);
287
- return includedFields;
288
- });
289
-
290
- const page = () => getStateVariants()?.page;
291
- const pageSize = () => getStateVariants()?.pageSize;
292
- const searchQuery = () => getStateVariants()?.searchQuery;
293
- const filterQueries = () => getStateVariants()?.filterQueries;
294
- const sort = () => getStateVariants()?.sort;
295
-
296
- const onPageChange = (page: number) => actions.setSampleVariantsPage(props.sample, page);
297
- const onSearchChange = (search: string) => actions.setSampleVariantsSearchQuery(props.sample, search);
298
- const onFilterChange = (event: FilterChangeEvent) =>
299
- actions.setSampleVariantsFilterQuery(props.sample, event.query, event.key);
300
- const onFilterClear = (event: FilterClearEvent) => actions.clearSampleVariantsFilterQuery(props.sample, event.key);
301
- const onSortChange = (event: SortEvent) => actions.setSampleVariantsSort(props.sample, event.order);
302
- const onSortClear = () => actions.setSampleVariantsSort(props.sample, null);
303
- const onRecordsPerPageChange = (event: RecordsPerPageEvent) =>
304
- actions.setSampleVariantsPageSize(props.sample, event.number);
305
-
306
- const params = (): Params => {
307
- return {
308
- query: createSampleQuery(props.sample, searchQuery(), filterQueries(), props.recordsMeta) || undefined,
309
- sort: sort() || undefined,
310
- page: page() || undefined,
311
- size: pageSize() || undefined,
312
- };
313
- };
314
-
315
- const [records] = createResource(params, fetchRecords);
316
-
317
- const sortOptions = () =>
318
- infoFields().flatMap((field) => [
319
- {
320
- order: { field, direction: DIRECTION_ASCENDING },
321
- selected:
322
- arrayEquals(infoSortPath(field), sort()?.property as SortPath) && sort()?.compare === DIRECTION_ASCENDING
323
- ? true
324
- : undefined,
325
- },
326
- {
327
- order: { field, direction: DIRECTION_DESCENDING },
328
- selected:
329
- arrayEquals(infoSortPath(field), sort()?.property as SortPath) && sort()?.compare === DIRECTION_DESCENDING
330
- ? true
331
- : undefined,
332
- },
333
- ]);
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
-
377
- return (
378
- <div class="variants columns is-variable is-1">
379
- <div class="scrolling-div column is-1-fullhd is-2">
380
- <SearchBox value={searchQuery()} onInput={onSearchChange} />
381
- <Filters
382
- fields={filterInfoFields()}
383
- samplesFields={[{ sample: props.sample, fields: formatFields() }]}
384
- queries={filterQueries()}
385
- onChange={onFilterChange}
386
- onClear={onFilterClear}
387
- />
388
- </div>
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>
398
- <div class="columns is-gapless">
399
- <div class="column is-offset-1-fullhd is-3-fullhd is-4">
400
- <Show when={records()} fallback={<Loader />} keyed>
401
- {(records) => (
402
- <span class="is-pulled-left inline-control-text ml-2">{records.page.totalElements} records</span>
403
- )}
404
- </Show>
405
- </div>
406
- <div class="column is-4">
407
- <Show when={records()} fallback={<Loader />} keyed>
408
- {(records) => <Pager page={records.page} onPageChange={onPageChange} />}
409
- </Show>
410
- </div>
411
- <div class="column">
412
- <div class="field is-grouped is-grouped-right">
413
- {infoFields().length > 0 && (
414
- <Sort options={sortOptions()} onChange={onSortChange} onClear={onSortClear} />
415
- )}
416
- <RecordDownload
417
- recordsMetadata={props.recordsMeta}
418
- query={params().query}
419
- samples={[props.sample.data, ...props.pedigreeSamples.map((item) => item.data)]}
420
- />
421
- </div>
422
- </div>
423
- </div>
424
- <div class="columns is-gapless">
425
- <div class="column is-full">
426
- <Show when={records()} fallback={<Loader />} keyed>
427
- {(records) => (
428
- <>
429
- <VariantsSampleTable
430
- item={props.sample}
431
- pedigreeSamples={props.pedigreeSamples}
432
- records={records.items}
433
- recordsMetadata={props.recordsMeta}
434
- nestedFields={infoFields()}
435
- htsFileMeta={props.htsFileMeta}
21
+ {(sample) => (
22
+ <>
23
+ <VariantBreadcrumb variantType={variantType()} sample={sample()} />
24
+ <Show when={config()} fallback={<Loader />}>
25
+ {(config) => (
26
+ <Show when={metadata()} fallback={<Loader />}>
27
+ {(metadata) => (
28
+ <VariantsContainer
29
+ store={wrapStore(store, sample(), variantType())}
30
+ config={config()}
31
+ metadata={metadata()}
32
+ variantType={variantType()}
33
+ sample={sample()}
436
34
  />
437
- <div class="columns is-gapless">
438
- <div class="column">
439
- <div class="field is-grouped is-grouped-right">
440
- <RecordsPerPage
441
- initialValue={getStateVariants()?.pageSize || 20}
442
- onChange={onRecordsPerPageChange}
443
- />
444
- </div>
445
- </div>
446
- </div>
447
- </>
448
- )}
449
- </Show>
450
- </div>
451
- </div>
452
- </div>
453
- </div>
35
+ )}
36
+ </Show>
37
+ )}
38
+ </Show>
39
+ </>
40
+ )}
41
+ </Show>
454
42
  );
455
43
  };
@@ -0,0 +1,20 @@
1
+ import { Component, Show } from "solid-js";
2
+ import { createAsync, Navigate, RouteSectionProps } from "@solidjs/router";
3
+ import { getSampleById } from "./data/data";
4
+ import { SampleContainer } from "../utils/api.ts";
5
+ import { href } from "../utils/utils.ts";
6
+
7
+ /**
8
+ * View that redirects to the variant type specific view based on the available variant types for a sample
9
+ */
10
+ export const SampleVariantsRedirect: Component<RouteSectionProps> = (props) => {
11
+ const sample = createAsync(() => getSampleById(props.params.sampleId));
12
+
13
+ function getHref(sample: SampleContainer) {
14
+ const variantTypeIds = sample.variantTypeIds;
15
+ const variantTypeId = variantTypeIds.size === 1 ? variantTypeIds.values().next().value! : "all";
16
+ return href(["samples", sample.item.id, "variants", variantTypeId]);
17
+ }
18
+
19
+ return <Show when={sample()}>{(sample) => <Navigate href={getHref(sample())} />}</Show>;
20
+ };
@@ -1,17 +1,16 @@
1
1
  import { Component, createResource, Show } from "solid-js";
2
2
  import { SampleTable } from "../components/SampleTable";
3
- import { Pager } from "../components/record/Pager";
3
+ import { PageChangeEvent, Pager } from "../components/Pager";
4
4
  import { SearchBox } from "../components/SearchBox";
5
- import { Checkbox, CheckboxEvent } from "../components/Checkbox";
6
5
  import { Breadcrumb } from "../components/Breadcrumb";
7
- import { EMPTY_PARAMS, EMPTY_PHENOTYPES, fetchPhenotypes, fetchSamples } from "../utils/ApiUtils";
8
6
  import { useStore } from "../store";
9
- import { Params, Query, QueryClause } from "@molgenis/vip-report-api/src/Api";
7
+ import { Params, Query, QueryClause } from "@molgenis/vip-report-api";
10
8
  import { Loader } from "../components/Loader";
9
+ import { fetchSamples } from "../utils/api.ts";
10
+ import { Checkbox, CheckboxEvent } from "../components/form/Checkbox.tsx";
11
11
 
12
12
  export const Samples: Component = () => {
13
13
  const [state, actions] = useStore();
14
- const [phenotypes] = createResource(EMPTY_PARAMS, fetchPhenotypes, { initialValue: EMPTY_PHENOTYPES });
15
14
 
16
15
  function getStateSamples() {
17
16
  return state.samples ? state.samples : undefined;
@@ -21,7 +20,7 @@ export const Samples: Component = () => {
21
20
  actions.setSamplePage(0);
22
21
  }
23
22
 
24
- const onPageChange = (page: number) => actions.setSamplePage(page);
23
+ const onPageChange = (event: PageChangeEvent) => actions.setSamplePage(event.page);
25
24
  const onSearchChange = (search: string) => {
26
25
  actions.setSampleSearchQuery(search);
27
26
  actions.setSamplePage(0);
@@ -49,11 +48,10 @@ export const Samples: Component = () => {
49
48
  args.push(probandQuery);
50
49
  }
51
50
  if (args.length > 0) {
52
- const query: Query = {
51
+ return {
53
52
  operator: "and",
54
53
  args: args,
55
54
  };
56
- return query;
57
55
  }
58
56
  }
59
57
  return null;
@@ -97,7 +95,6 @@ export const Samples: Component = () => {
97
95
  <div class="control">
98
96
  <Checkbox
99
97
  value={"proband"}
100
- label=""
101
98
  onChange={onProbandFilterChange}
102
99
  checked={state.samples?.probandFilterValue}
103
100
  />
@@ -105,7 +102,7 @@ export const Samples: Component = () => {
105
102
  </div>
106
103
  </div>
107
104
  <div class="column">
108
- {!phenotypes.loading && <SampleTable samples={samples.items} phenotypes={phenotypes().items} />}
105
+ <SampleTable samples={samples.items} />
109
106
  </div>
110
107
  </div>
111
108
  </>
@@ -1,69 +1,39 @@
1
- import { Component, createResource, For, Show } from "solid-js";
1
+ import { Component, Show } from "solid-js";
2
2
  import { createAsync, RouteSectionProps } from "@solidjs/router";
3
- import { GenomeBrowser } from "../components/GenomeBrowser";
4
3
  import { Loader } from "../components/Loader";
5
- import { Value } from "@molgenis/vip-report-vcf/src/ValueParser";
6
- import { VariantTable } from "../components/VariantTable";
7
- import { VariantInfoTable } from "../components/VariantInfoTable";
8
- import { VariantInfoNestedTable } from "../components/VariantInfoNestedTable";
9
- import { getNestedInfoFieldsWithValues } from "../utils/field";
10
- import { Breadcrumb } from "../components/Breadcrumb";
11
- import { EMPTY_RECORDS_METADATA, fetchRecordsMeta, getRecordLabel } from "../utils/ApiUtils";
12
- import { getVariant } from "./data/data";
13
- import { Item } from "@molgenis/vip-report-api/src/Api";
14
- import { Record } from "@molgenis/vip-report-vcf/src/Vcf";
4
+ import { getConfig, getMetadata, getRecordById } from "./data/data";
5
+ import { parseVariantType } from "../utils/variantType.ts";
6
+ import { VariantContainer } from "../components/VariantContainer.tsx";
7
+ import { VariantBreadcrumb } from "../components/VariantBreadcrumb.tsx";
15
8
 
16
- export const Variant: Component<RouteSectionProps<Promise<Item<Record>>>> = (props) => {
17
- const variant = createAsync(() => getVariant(Number(props.params.variantId)));
18
-
19
- const [recordsMetadata] = createResource(fetchRecordsMeta, { initialValue: EMPTY_RECORDS_METADATA });
9
+ export const Variant: Component<RouteSectionProps> = (props) => {
10
+ const variantType = () => parseVariantType(props.params.variantType);
11
+ const record = createAsync(() => getRecordById(props.params.variantId));
12
+ const metadata = createAsync(() => getMetadata());
13
+ const config = createAsync(() => getConfig());
20
14
 
21
15
  return (
22
- <>
23
- <Show when={variant()} fallback={<Loader />}>
24
- {(variant) => (
25
- <>
26
- <Breadcrumb items={[{ href: "/variants", text: "Variants" }, { text: getRecordLabel(variant()) }]} />
27
- <GenomeBrowser contig={variant().data.c} position={variant().data.p} samples={[]} />
28
- <div class="columns">
29
- <div class="column is-3">
30
- <h1 class="title is-5">Record</h1>
31
- <VariantTable variant={variant().data} />
32
- </div>
33
- <Show
34
- when={
35
- Object.values(recordsMetadata().info).filter(
36
- (info) => !info.nested && variant().data.n[info.id] !== undefined,
37
- ).length > 0
38
- }
39
- >
40
- <div class="column is-3">
41
- <h1 class="title is-5">Info</h1>
42
- <VariantInfoTable infoFields={recordsMetadata().info} record={variant()} />
43
- </div>
16
+ <Show when={record()} fallback={<Loader />}>
17
+ {(record) => (
18
+ <>
19
+ <VariantBreadcrumb variantType={variantType()} record={record()} />
20
+ <Show when={metadata()} fallback={<Loader />}>
21
+ {(metadata) => (
22
+ <Show when={config()} fallback={<Loader />}>
23
+ {(config) => (
24
+ <VariantContainer
25
+ config={config()}
26
+ metadata={metadata()}
27
+ variantType={variantType()}
28
+ record={record()}
29
+ sample={null}
30
+ />
31
+ )}
44
32
  </Show>
45
- </div>
46
- <div class="columns">
47
- <div class="column" style={{ "max-width": "100%" }}>
48
- <For each={getNestedInfoFieldsWithValues(recordsMetadata().info, variant().data.n)}>
49
- {(infoField) => (
50
- <>
51
- <h1 class="title is-5">{infoField.id}</h1>
52
- <p class="mb-4">{infoField.description}</p>
53
- <VariantInfoNestedTable
54
- infoValue={variant().data.n[infoField.id] as unknown as Value[][]}
55
- infoField={infoField}
56
- record={variant()}
57
- sample={null}
58
- />
59
- </>
60
- )}
61
- </For>
62
- </div>
63
- </div>
64
- </>
65
- )}
66
- </Show>
67
- </>
33
+ )}
34
+ </Show>
35
+ </>
36
+ )}
37
+ </Show>
68
38
  );
69
39
  };