@molgenis/vip-report-template 6.1.1 → 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 +9 -11
  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 +35 -29
  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 +8 -5
  12. package/src/components/DatasetDropdown.tsx +10 -23
  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 +143 -0
  119. package/src/mocks/GRCh37/static.ts +63 -133
  120. package/src/mocks/GRCh37/vcf/family.vcf.blob +37 -31
  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 +341 -328
  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 -114
  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 -183
  237. package/src/components/VariantsTable.tsx +0 -124
  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 -259
  281. package/src/utils/csqUtils.ts +0 -27
  282. package/src/utils/decisionTreeUtils.ts +0 -14
  283. package/src/utils/field.ts +0 -49
  284. package/src/utils/query.ts +0 -154
  285. package/src/utils/viewUtils.ts +0 -32
@@ -0,0 +1,32 @@
1
+ import { Component, Show } from "solid-js";
2
+ import { FieldTypedMultiple } from "../typed/FieldTypedMultiple";
3
+ import { CellValueInheritanceModes } from "../../../types/configCellComposed";
4
+ import { ValueCategorical } from "../../../utils/vcf.ts";
5
+
6
+ export const FieldInheritanceModes: Component<{ value: CellValueInheritanceModes }> = (props) => {
7
+ const inheritanceModesGene = (): ValueCategorical[] => props.value.inheritanceModesGene;
8
+ const isArInheritance = () => {
9
+ let isArInheritance = false;
10
+ if (
11
+ (inheritanceModesGene() !== undefined && inheritanceModesGene().length === 0) ||
12
+ inheritanceModesGene().findIndex((category) => category?.value === "AR") !== -1
13
+ ) {
14
+ isArInheritance =
15
+ props.value.isPossibleCompound !== undefined &&
16
+ props.value.isPossibleCompound !== null &&
17
+ props.value.isPossibleCompound;
18
+ }
19
+ return isArInheritance;
20
+ };
21
+
22
+ return (
23
+ <>
24
+ <FieldTypedMultiple info={inheritanceModesGene()} infoMetadata={props.value.fieldInheritanceModesGene} />
25
+ <Show when={isArInheritance()} keyed>
26
+ <abbr title="Variant is possibly part of a compound." class="ml-1 is-clickable">
27
+ <i class="fas fa-circle-exclamation has-text-info" />
28
+ </abbr>
29
+ </Show>
30
+ </>
31
+ );
32
+ };
@@ -0,0 +1,18 @@
1
+ import { Component } from "solid-js";
2
+ import { useNavigate } from "@solidjs/router";
3
+ import { FieldChrom } from "../FieldChrom";
4
+ import { FieldPos } from "../FieldPos";
5
+ import { CellValueLocus } from "../../../types/configCellComposed";
6
+
7
+ export const FieldLocus: Component<{ value: CellValueLocus }> = (props) => {
8
+ // use 'navigate' instead of '<A href=' such that cache is used
9
+ const navigate = useNavigate();
10
+
11
+ return (
12
+ <a onClick={() => navigate(props.value.href)}>
13
+ <FieldChrom value={props.value.c} />
14
+ <span>:</span>
15
+ <FieldPos value={props.value.p} />
16
+ </a>
17
+ );
18
+ };
@@ -0,0 +1,25 @@
1
+ import { Component, Show } from "solid-js";
2
+ import { CellValueVipC } from "../../../types/configCellComposed";
3
+ import { Abbr } from "../../Abbr";
4
+ import { useNavigate } from "@solidjs/router";
5
+
6
+ export const FieldVipC: Component<{
7
+ value: CellValueVipC;
8
+ }> = (props) => {
9
+ const navigate = useNavigate();
10
+
11
+ const onClick = () => {
12
+ if (props.value.href) {
13
+ // use 'navigate' instead of '<A href=' such that cache is used
14
+ navigate(props.value.href);
15
+ }
16
+ };
17
+
18
+ return (
19
+ <a onClick={onClick}>
20
+ <Show when={props.value.vipP} fallback={<span>{props.value.vipC!.value}</span>} keyed>
21
+ {(vipPItem) => <Abbr title={vipPItem.join(", ")} value={props.value.vipC!.value!} />}
22
+ </Show>
23
+ </a>
24
+ );
25
+ };
@@ -0,0 +1,15 @@
1
+ import { Component, Show } from "solid-js";
2
+ import { CellValueVipCS } from "../../../types/configCellComposed";
3
+ import { Abbr } from "../../Abbr";
4
+
5
+ export const FieldVipCS: Component<{
6
+ value: CellValueVipCS;
7
+ }> = (props) => {
8
+ return (
9
+ <Show when={props.value.vipCS !== null}>
10
+ <Show when={props.value.vipPS} fallback={<span>{props.value.vipCS!.value}</span>} keyed>
11
+ {(vipPSItem) => <Abbr title={vipPSItem.join(", ")} value={props.value.vipCS!.value!} />}
12
+ </Show>
13
+ </Show>
14
+ );
15
+ };
@@ -0,0 +1,37 @@
1
+ import { Component, Show } from "solid-js";
2
+ import { Abbr } from "../../Abbr";
3
+ import { CellValueVkgl } from "../../../types/configCellComposed";
4
+ import { ValueCategorical } from "../../../utils/vcf.ts";
5
+
6
+ export const FieldVkgl: Component<{ value: CellValueVkgl }> = (props) => {
7
+ const label = (): string | null => props.value.vkglCl?.label || null;
8
+ const title = () => {
9
+ if (label() === null) return undefined;
10
+
11
+ const labValues: Record<string, ValueCategorical | undefined> = {
12
+ AMC: props.value.vkglAmc,
13
+ Erasmus: props.value.vkglErasmus,
14
+ LUMC: props.value.vkglLumc,
15
+ NKI: props.value.vkglNki,
16
+ "Radboud/MUMC": props.value.vkglRadboudMumc,
17
+ UMCG: props.value.vkglUmcg,
18
+ UMCU: props.value.vkglUmcu,
19
+ VUMC: props.value.vkglVumc,
20
+ };
21
+
22
+ const descriptions = Object.entries(labValues)
23
+ .filter(([, value]) => value !== null && value !== undefined)
24
+ .map(([key, value]) => `${key}:${value?.label}`);
25
+
26
+ return descriptions.length > 0 ? descriptions.join(", ") : null;
27
+ };
28
+ return (
29
+ <Show when={label()} keyed>
30
+ {(value) => (
31
+ <Show when={title()} fallback={<span>{value}</span>} keyed>
32
+ {(title) => <Abbr title={title} value={value} />}
33
+ </Show>
34
+ )}
35
+ </Show>
36
+ );
37
+ };
@@ -0,0 +1,19 @@
1
+ import { Component, Match, Switch } from "solid-js";
2
+ import { FieldTyped } from "../typed/FieldTyped";
3
+ import { FieldGenotypeType } from "./FieldGenotypeType";
4
+ import { FieldMetadata, Genotype, Value } from "@molgenis/vip-report-vcf";
5
+ import { CellValueGenotype } from "../../../types/configCells";
6
+
7
+ export const FieldGenotype: Component<{
8
+ metadata: FieldMetadata;
9
+ value: CellValueGenotype;
10
+ }> = (props) => {
11
+ // add genotype fields with composed rendering here
12
+ return (
13
+ <Switch fallback={<FieldTyped metadata={props.metadata} value={props.value as Value} />}>
14
+ <Match when={props.metadata.id === "GT"}>
15
+ <FieldGenotypeType value={props.value as Genotype} />
16
+ </Match>
17
+ </Switch>
18
+ );
19
+ };
@@ -0,0 +1,9 @@
1
+ import { Component } from "solid-js";
2
+ import { Genotype } from "@molgenis/vip-report-vcf";
3
+
4
+ export const FieldGenotypeType: Component<{
5
+ value: Genotype;
6
+ }> = (props) => {
7
+ const displayValue = () => props.value.a.join(props.value.p ? "|" : "/");
8
+ return <span>{displayValue()}</span>;
9
+ };
@@ -0,0 +1,15 @@
1
+ import { Component } from "solid-js";
2
+ import { Abbr } from "../../Abbr";
3
+
4
+ export const FieldConsequence: Component<{ value: string[] }> = (props) => {
5
+ return (
6
+ <>
7
+ <span>{props.value[0]}</span>
8
+ {props.value.length > 1 && (
9
+ <span>
10
+ , <Abbr title={props.value.slice(1).join(", ")} value={"\u2026"} />
11
+ </span>
12
+ )}
13
+ </>
14
+ );
15
+ };
@@ -1,23 +1,21 @@
1
1
  import { Component, Show } from "solid-js";
2
2
  import { Abbr } from "../../Abbr";
3
- import { FieldProps } from "../field/Field";
4
- import { ValueString } from "@molgenis/vip-report-vcf/src/ValueParser";
3
+ import { ValueString } from "@molgenis/vip-report-vcf";
5
4
 
6
5
  function abbreviate(notation: string): string {
7
6
  let abbreviated;
8
7
  const tokens = notation.split(":");
9
8
  if (tokens.length === 2) {
10
- abbreviated = tokens[1];
9
+ abbreviated = tokens[1]!;
11
10
  } else {
12
11
  abbreviated = notation;
13
12
  }
14
13
  return abbreviated.length > 20 ? abbreviated.slice(0, 18) + "\u2026" : abbreviated;
15
14
  }
16
15
 
17
- export const Hgvs: Component<FieldProps> = (props) => {
18
- const value = () => props.info.value as ValueString;
16
+ export const FieldHgvs: Component<{ value: ValueString }> = (props) => {
19
17
  return (
20
- <Show when={value()} keyed>
18
+ <Show when={props.value} keyed>
21
19
  {(value) => <Abbr title={value} value={abbreviate(value)} />}
22
20
  </Show>
23
21
  );
@@ -0,0 +1,27 @@
1
+ import { Component, Match, Switch } from "solid-js";
2
+ import { FieldConsequence } from "./FieldConsequence";
3
+ import { FieldTyped } from "../typed/FieldTyped";
4
+ import { FieldPubMed } from "./FieldPubMed";
5
+ import { FieldHgvs } from "./FieldHgvs";
6
+ import { isAnyCsqInfo, isCsqInfo } from "../../../utils/csq.ts";
7
+ import { InfoMetadata, ValueString } from "@molgenis/vip-report-vcf";
8
+ import { CellValueInfo } from "../../../types/configCells";
9
+
10
+ export const FieldInfo: Component<{
11
+ metadata: InfoMetadata;
12
+ value: CellValueInfo;
13
+ }> = (props) => {
14
+ return (
15
+ <Switch fallback={<FieldTyped metadata={props.metadata} value={props.value} />}>
16
+ <Match when={isCsqInfo(props.metadata, "Consequence")}>
17
+ <FieldConsequence value={props.value as string[]} />
18
+ </Match>
19
+ <Match when={isCsqInfo(props.metadata, "PUBMED")}>
20
+ <FieldPubMed value={props.value as number[]} />
21
+ </Match>
22
+ <Match when={isAnyCsqInfo(props.metadata, ["HGVSc", "HGVSp"])}>
23
+ <FieldHgvs value={props.value as ValueString} />
24
+ </Match>
25
+ </Switch>
26
+ );
27
+ };
@@ -1,12 +1,9 @@
1
1
  import { Component, Show } from "solid-js";
2
2
  import { Anchor } from "../../Anchor";
3
- import { FieldProps } from "../field/Field";
4
-
5
- export const PubMed: Component<FieldProps> = (props) => {
6
- const pubmedIds = () => props.info.value as number[];
7
3
 
4
+ export const FieldPubMed: Component<{ value: number[] }> = (props) => {
8
5
  const href = () => {
9
- const ids = pubmedIds();
6
+ const ids = props.value;
10
7
  if (ids) {
11
8
  const term = ids.map((id) => `${id}[UID]`).join("+OR+");
12
9
  return `https://pubmed.ncbi.nlm.nih.gov/?term=${term}&sort=pubdate`;
@@ -14,9 +11,9 @@ export const PubMed: Component<FieldProps> = (props) => {
14
11
  };
15
12
 
16
13
  return (
17
- <Show when={pubmedIds().length > 0}>
14
+ <Show when={props.value.length > 0}>
18
15
  <Anchor href={href()}>
19
- <span>{`citations (${pubmedIds().length})`}</span>
16
+ <span>{`citations (${props.value.length})`}</span>
20
17
  </Anchor>
21
18
  </Show>
22
19
  );
@@ -0,0 +1,17 @@
1
+ import { Component, Show } from "solid-js";
2
+ import { Abbr } from "../../Abbr";
3
+ import { ValueCategorical } from "../../../utils/vcf.ts";
4
+ import { FieldMetadata } from "@molgenis/vip-report-vcf";
5
+
6
+ export const FieldCategorical: Component<{
7
+ value: ValueCategorical | undefined;
8
+ metadata: FieldMetadata;
9
+ }> = (props) => {
10
+ return (
11
+ <Show when={props.value !== null && props.value !== undefined}>
12
+ <Show when={props.value!.description} fallback={<span>{props.value!.label}</span>}>
13
+ {(description) => <Abbr title={description()} value={props.value!.label} />}
14
+ </Show>
15
+ </Show>
16
+ );
17
+ };
@@ -1,7 +1,8 @@
1
1
  import { Component, Show } from "solid-js";
2
+ import { ValueString } from "@molgenis/vip-report-vcf";
2
3
 
3
- export const FieldValueCharacter: Component<{
4
- value: string | null | undefined;
4
+ export const FieldCharacter: Component<{
5
+ value: ValueString | undefined;
5
6
  }> = (props) => {
6
7
  return (
7
8
  <Show when={props.value !== null && props.value !== undefined}>
@@ -1,7 +1,8 @@
1
1
  import { Component, Show } from "solid-js";
2
+ import { ValueFlag } from "@molgenis/vip-report-vcf";
2
3
 
3
- export const FieldValueFlag: Component<{
4
- value: boolean | null | undefined;
4
+ export const FieldFlag: Component<{
5
+ value: ValueFlag | undefined;
5
6
  }> = (props) => {
6
7
  return (
7
8
  <Show when={props.value !== null && props.value !== undefined}>
@@ -1,8 +1,9 @@
1
1
  import { Component, Show } from "solid-js";
2
2
  import { Abbr } from "../../Abbr";
3
+ import { ValueFloat } from "@molgenis/vip-report-vcf";
3
4
 
4
- export const FieldValueFloat: Component<{
5
- value: number | null | undefined;
5
+ export const FieldFloat: Component<{
6
+ value: ValueFloat | undefined;
6
7
  }> = (props) => {
7
8
  return (
8
9
  <Show when={props.value !== null && props.value !== undefined}>
@@ -1,7 +1,8 @@
1
1
  import { Component, Show } from "solid-js";
2
+ import { ValueInteger } from "@molgenis/vip-report-vcf";
2
3
 
3
- export const FieldValueInteger: Component<{
4
- value: number | null | undefined;
4
+ export const FieldInteger: Component<{
5
+ value: ValueInteger | undefined;
5
6
  }> = (props) => {
6
7
  return (
7
8
  <Show when={props.value !== null && props.value !== undefined}>
@@ -1,8 +1,9 @@
1
1
  import { Component, Show } from "solid-js";
2
2
  import { Abbr } from "../../Abbr";
3
+ import { ValueString } from "@molgenis/vip-report-vcf";
3
4
 
4
- export const FieldValueString: Component<{
5
- value: string | null | undefined;
5
+ export const FieldString: Component<{
6
+ value: ValueString | undefined;
6
7
  }> = (props) => {
7
8
  return (
8
9
  <Show when={props.value !== null && props.value !== undefined}>
@@ -0,0 +1,20 @@
1
+ import { Component } from "solid-js";
2
+ import { FieldTypedItem, FieldValueSingle } from "./FieldTypedItem";
3
+ import { FieldTypedMultiple } from "./FieldTypedMultiple";
4
+ import { FieldMetadata } from "@molgenis/vip-report-vcf";
5
+ import { CellValueFormat, CellValueInfo } from "../../../types/configCells";
6
+
7
+ export const FieldTyped: Component<{
8
+ metadata: FieldMetadata;
9
+ value: CellValueInfo | CellValueFormat;
10
+ }> = (props) => {
11
+ return (
12
+ <>
13
+ {props.metadata.number.count === 1 ? (
14
+ <FieldTypedItem value={props.value as FieldValueSingle | undefined} metadata={props.metadata} />
15
+ ) : (
16
+ <FieldTypedMultiple info={props.value as FieldValueSingle[]} infoMetadata={props.metadata} />
17
+ )}
18
+ </>
19
+ );
20
+ };
@@ -0,0 +1,49 @@
1
+ import { Component, Match, Switch } from "solid-js";
2
+ import {
3
+ FieldMetadata,
4
+ ValueCharacter,
5
+ ValueFlag,
6
+ ValueFloat,
7
+ ValueInteger,
8
+ ValueObject,
9
+ ValueString,
10
+ } from "@molgenis/vip-report-vcf";
11
+ import { FieldFloat } from "./FieldFloat";
12
+ import { FieldString } from "./FieldString";
13
+ import { FieldCharacter } from "./FieldCharacter";
14
+ import { FieldInteger } from "./FieldInteger";
15
+ import { FieldFlag } from "./FieldFlag";
16
+ import { ErrorNotification } from "../../ErrorNotification";
17
+ import { FieldCategorical } from "./FieldCategorical";
18
+ import { ValueCategorical } from "../../../utils/vcf.ts";
19
+
20
+ export type FieldValueSingle = ValueCharacter | ValueFlag | ValueFloat | ValueInteger | ValueObject | ValueString;
21
+
22
+ export const FieldTypedItem: Component<{
23
+ value: FieldValueSingle | undefined;
24
+ metadata: FieldMetadata;
25
+ }> = (props) => {
26
+ const type = () => props.metadata.type;
27
+ return (
28
+ <Switch fallback={<ErrorNotification error={`unexpected field type ${type()}`} />}>
29
+ <Match when={type() === "CATEGORICAL"}>
30
+ <FieldCategorical value={props.value as ValueCategorical | undefined} metadata={props.metadata} />
31
+ </Match>
32
+ <Match when={type() === "CHARACTER"}>
33
+ <FieldCharacter value={props.value as ValueString | undefined} />
34
+ </Match>
35
+ <Match when={type() === "FLAG"}>
36
+ <FieldFlag value={props.value as ValueFlag | undefined} />
37
+ </Match>
38
+ <Match when={type() === "FLOAT"}>
39
+ <FieldFloat value={props.value as ValueFloat | undefined} />
40
+ </Match>
41
+ <Match when={type() === "INTEGER"}>
42
+ <FieldInteger value={props.value as ValueInteger | undefined} />
43
+ </Match>
44
+ <Match when={type() === "STRING"}>
45
+ <FieldString value={props.value as ValueString | undefined} />
46
+ </Match>
47
+ </Switch>
48
+ );
49
+ };
@@ -0,0 +1,21 @@
1
+ import { Component, For } from "solid-js";
2
+ import { FieldMetadata } from "@molgenis/vip-report-vcf";
3
+ import { FieldTypedItem, FieldValueSingle } from "./FieldTypedItem";
4
+
5
+ export const FieldTypedMultiple: Component<{
6
+ info: FieldValueSingle[];
7
+ infoMetadata: FieldMetadata;
8
+ }> = (props) => {
9
+ return (
10
+ <>
11
+ <For each={props.info}>
12
+ {(value, i) => (
13
+ <>
14
+ {i() !== 0 && <span>, </span>}
15
+ <FieldTypedItem value={value} metadata={props.infoMetadata} />
16
+ </>
17
+ )}
18
+ </For>
19
+ </>
20
+ );
21
+ };
@@ -1,54 +1,62 @@
1
1
  import { Component, Match, Switch } from "solid-js";
2
- import { FieldMetadata } from "@molgenis/vip-report-vcf/src/MetadataParser";
3
- import { FilterCategorical } from "./FilterCategorical";
4
- import { FilterIntegerGq } from "./FilterIntegerGq";
5
- import { Item, Query, Sample } from "@molgenis/vip-report-api/src/Api";
6
- import { FilterClinVar } from "./FilterClinVar";
7
- import { isAnyCsqInfo } from "../../utils/csqUtils";
8
- import { FilterChangeEvent, FilterClearEvent } from "./Filters";
9
- import { FilterAllelicBalance } from "./FilterAllelicBalance";
10
- import { FilterHpo } from "./FilterHpo";
11
- import { FilterGene } from "./FilterGene";
12
- import { FilterVI } from "./FilterVI";
13
- import { FilterVariantType } from "./FilterVariantType";
2
+ import {
3
+ ConfigFilter,
4
+ ConfigFilterBase,
5
+ ConfigFilterField,
6
+ ConfigFilterFixed,
7
+ FilterValue,
8
+ FilterValueField,
9
+ FilterValueFixed,
10
+ } from "../../types/configFilter";
11
+ import { FilterTyped } from "./typed/FilterTyped";
12
+ import { ConfigFilterComposed, FilterValueComposed } from "../../types/configFilterComposed";
13
+ import { FilterComposed } from "./composed/FilterComposed";
14
+ import { ErrorNotification } from "../ErrorNotification";
15
+ import { FilterFixed } from "./fixed/FilterFixed.tsx";
14
16
 
15
- export type FilterProps = {
16
- field: FieldMetadata;
17
- query?: Query;
18
- onChange: (event: FilterChangeEvent) => void;
19
- onClear: (event: FilterClearEvent) => void;
20
- sample?: Item<Sample>;
21
- };
17
+ export interface FilterValueChangeEvent<FilterValueType> {
18
+ value: FilterValueType;
19
+ }
20
+
21
+ export type FilterValueChangeCallback<FilterValueType> = (event: FilterValueChangeEvent<FilterValueType>) => void;
22
+ export type FilterValueClearCallback = () => void;
23
+
24
+ export interface FilterProps<C extends ConfigFilterBase, FilterValueType> {
25
+ config: C;
26
+ value?: FilterValueType;
27
+ onValueChange: FilterValueChangeCallback<FilterValueType>;
28
+ onValueClear: FilterValueClearCallback;
29
+ }
30
+
31
+ export const Filter: Component<FilterProps<ConfigFilter, FilterValue>> = (props) => {
32
+ const type = () => props.config.type;
22
33
 
23
- export const Filter: Component<FilterProps> = (props) => {
24
34
  return (
25
- <>
26
- <Switch>
27
- <Match when={props.field.id === "GQ"}>
28
- <FilterIntegerGq {...props} />
29
- </Match>
30
- <Match when={props.field.id === "VIAB"}>
31
- <FilterAllelicBalance {...props} />
32
- </Match>
33
- <Match when={props.field.id === "VI"}>
34
- <FilterVI {...props} />
35
- </Match>
36
- <Match when={props.field.id === "SVTYPE"}>
37
- <FilterVariantType {...props} />
38
- </Match>
39
- <Match when={isAnyCsqInfo(props.field, ["clinVar_CLNSIG", "clinVar_CLNSIGINCL"])}>
40
- <FilterClinVar {...props} />
41
- </Match>
42
- <Match when={props.field.id === "HPO"}>
43
- <FilterHpo {...props} />
44
- </Match>
45
- <Match when={props.field.id === "IncompletePenetrance"}>
46
- <FilterGene {...props} />
47
- </Match>
48
- <Match when={props.field.type === "CATEGORICAL"}>
49
- <FilterCategorical {...props} />
50
- </Match>
51
- </Switch>
52
- </>
35
+ <Switch fallback={<ErrorNotification error={`unexpected field type ${type()}`} />}>
36
+ <Match when={type() === "fixed"}>
37
+ <FilterFixed
38
+ config={props.config as ConfigFilterFixed}
39
+ value={props.value as FilterValueFixed}
40
+ onValueChange={props.onValueChange}
41
+ onValueClear={props.onValueClear}
42
+ />
43
+ </Match>
44
+ <Match when={type() === "info" || type() === "genotype"}>
45
+ <FilterTyped
46
+ config={props.config as ConfigFilterField}
47
+ value={props.value as FilterValueField}
48
+ onValueChange={props.onValueChange}
49
+ onValueClear={props.onValueClear}
50
+ />
51
+ </Match>
52
+ <Match when={type() === "composed"}>
53
+ <FilterComposed
54
+ config={props.config as ConfigFilterComposed}
55
+ value={props.value as FilterValueComposed}
56
+ onValueChange={props.onValueChange}
57
+ onValueClear={props.onValueClear}
58
+ />
59
+ </Match>
60
+ </Switch>
53
61
  );
54
62
  };
@@ -0,0 +1,23 @@
1
+ .filter {
2
+ padding: 0 0 1rem;
3
+ }
4
+
5
+ .filter-header {
6
+ display: flex;
7
+ align-items: stretch;
8
+ padding: 0 0 0.5rem;
9
+ border-bottom: 1px solid var(--bulma-hr-background-color);
10
+ }
11
+
12
+ .filter-header-title {
13
+ flex-grow: 1;
14
+ font-weight: var(--bulma-weight-semibold);
15
+ }
16
+
17
+ .filter-header-icon {
18
+ cursor: pointer
19
+ }
20
+
21
+ .filter-container {
22
+ padding: 0.5rem 0 0;
23
+ }
@@ -0,0 +1,63 @@
1
+ import "./FilterWrapper.scss";
2
+ import { createSignal, JSX, ParentComponent, Show, Signal } from "solid-js";
3
+ import { ConfigFilterBase } from "../../types/configFilter";
4
+ import { Tooltip } from "../Tooltip.tsx";
5
+
6
+ export const FilterWrapper: ParentComponent<{
7
+ config: ConfigFilterBase;
8
+ error?: string | undefined;
9
+ tooltipContentElement?: JSX.Element; // additional tooltip content
10
+ }> = (props) => {
11
+ const [collapsed, setCollapsed]: Signal<boolean> = createSignal(false);
12
+ const [showTooltip, setShowTooltip]: Signal<boolean> = createSignal(false);
13
+
14
+ const isCollapsible = () => false; // TODO add to config filter base
15
+ const label = () => props.config.label();
16
+ const description = () => props.config.description();
17
+ const hasTooltipContent = () => description() || props.tooltipContentElement;
18
+
19
+ function toggleShowTooltip() {
20
+ setShowTooltip(!showTooltip());
21
+ }
22
+
23
+ function toggleCollapse() {
24
+ setCollapsed(!collapsed());
25
+ }
26
+
27
+ const ErrorMessage = (props: { error: string | undefined }) => (
28
+ <div class="notification is-danger is-light">{props.error}</div>
29
+ );
30
+
31
+ return (
32
+ <div class="filter">
33
+ <header class="filter-header">
34
+ <p class="filter-header-title">{label()}</p>
35
+ <Show when={hasTooltipContent()}>
36
+ <button class="filter-header-icon" onClick={toggleShowTooltip}>
37
+ <span class="icon">
38
+ <i class="fas fa-info" />
39
+ </span>
40
+ <Show when={showTooltip()}>
41
+ <Tooltip text={description()}>{props.tooltipContentElement}</Tooltip>
42
+ </Show>
43
+ </button>
44
+ </Show>
45
+ <Show when={isCollapsible()}>
46
+ <button class="filter-header-icon" onClick={toggleCollapse}>
47
+ <span class="icon">
48
+ <Show when={collapsed()} fallback={<i class="fas fa-angle-up" />}>
49
+ <i class="fas fa-angle-down" />
50
+ </Show>
51
+ </span>
52
+ </button>
53
+ </Show>
54
+ </header>
55
+ <Show when={props.error !== undefined}>
56
+ <ErrorMessage error={props.error} />
57
+ </Show>
58
+ <Show when={!collapsed()}>
59
+ <div class="filter-container">{props.children}</div>
60
+ </Show>
61
+ </div>
62
+ );
63
+ };