@genspectrum/dashboard-components 0.19.3 → 0.19.5

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 (29) hide show
  1. package/custom-elements.json +223 -0
  2. package/dist/{LineageFilterChangedEvent-b0iuroUL.js → LineageFilterChangedEvent-GgkxoF3X.js} +4 -2
  3. package/dist/{LineageFilterChangedEvent-b0iuroUL.js.map → LineageFilterChangedEvent-GgkxoF3X.js.map} +1 -1
  4. package/dist/components.d.ts +137 -20
  5. package/dist/components.js +589 -241
  6. package/dist/components.js.map +1 -1
  7. package/dist/util.d.ts +55 -20
  8. package/dist/util.js +1 -1
  9. package/package.json +1 -1
  10. package/src/preact/components/min-max-range-slider.tsx +19 -4
  11. package/src/preact/dateRangeFilter/date-range-filter.stories.tsx +4 -1
  12. package/src/preact/genomeViewer/loadGff3.spec.ts +1 -1
  13. package/src/preact/genomeViewer/loadGff3.ts +12 -6
  14. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +4 -2
  15. package/src/preact/numberRangeFilter/NumberRangeFilterChangedEvent.ts +31 -0
  16. package/src/preact/numberRangeFilter/number-range-filter.stories.tsx +383 -0
  17. package/src/preact/numberRangeFilter/number-range-filter.tsx +159 -0
  18. package/src/preact/numberRangeFilter/useSelectedRangeReducer.ts +137 -0
  19. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +35 -1
  20. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +40 -3
  21. package/src/utilEntrypoint.ts +2 -0
  22. package/src/utils/gsEventNames.ts +2 -0
  23. package/src/web-components/input/gs-number-range-filter.spec.ts +27 -0
  24. package/src/web-components/input/gs-number-range-filter.stories.ts +96 -0
  25. package/src/web-components/input/gs-number-range-filter.tsx +148 -0
  26. package/src/web-components/input/gs-text-filter.stories.ts +2 -2
  27. package/src/web-components/input/index.ts +1 -0
  28. package/standalone-bundle/dashboard-components.js +6991 -6688
  29. package/standalone-bundle/dashboard-components.js.map +1 -1
package/dist/util.d.ts CHANGED
@@ -157,6 +157,8 @@ export declare const gsEventNames: {
157
157
  readonly lineageFilterChanged: "gs-lineage-filter-changed";
158
158
  readonly locationChanged: "gs-location-changed";
159
159
  readonly textFilterChanged: "gs-text-filter-changed";
160
+ readonly numberRangeFilterChanged: "gs-number-range-filter-changed";
161
+ readonly numberRangeValueChanged: "gs-number-range-value-changed";
160
162
  };
161
163
 
162
164
  export declare type LapisFilter = default_2.infer<typeof lapisFilterSchema>;
@@ -484,6 +486,19 @@ declare const namedLapisFilterSchema: default_2.ZodObject<{
484
486
  displayName: string;
485
487
  }>;
486
488
 
489
+ export declare type NumberRange = default_2.infer<typeof numberRangeSchema>;
490
+
491
+ declare const numberRangeSchema: default_2.ZodObject<{
492
+ min: default_2.ZodOptional<default_2.ZodNumber>;
493
+ max: default_2.ZodOptional<default_2.ZodNumber>;
494
+ }, "strip", default_2.ZodTypeAny, {
495
+ min?: number | undefined;
496
+ max?: number | undefined;
497
+ }, {
498
+ min?: number | undefined;
499
+ max?: number | undefined;
500
+ }>;
501
+
487
502
  export declare type NumberSequencesOverTimeProps = default_2.infer<typeof numberSequencesOverTimePropsSchema>;
488
503
 
489
504
  declare const numberSequencesOverTimePropsSchema: default_2.ZodObject<{
@@ -960,7 +975,7 @@ declare global {
960
975
 
961
976
  declare global {
962
977
  interface HTMLElementTagNameMap {
963
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
978
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
964
979
  }
965
980
  }
966
981
 
@@ -968,7 +983,7 @@ declare global {
968
983
  declare global {
969
984
  namespace JSX {
970
985
  interface IntrinsicElements {
971
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
986
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
972
987
  }
973
988
  }
974
989
  }
@@ -976,7 +991,7 @@ declare global {
976
991
 
977
992
  declare global {
978
993
  interface HTMLElementTagNameMap {
979
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
994
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
980
995
  }
981
996
  }
982
997
 
@@ -984,7 +999,7 @@ declare global {
984
999
  declare global {
985
1000
  namespace JSX {
986
1001
  interface IntrinsicElements {
987
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1002
+ 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
988
1003
  }
989
1004
  }
990
1005
  }
@@ -992,7 +1007,7 @@ declare global {
992
1007
 
993
1008
  declare global {
994
1009
  interface HTMLElementTagNameMap {
995
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1010
+ 'gs-aggregate': AggregateComponent;
996
1011
  }
997
1012
  }
998
1013
 
@@ -1000,7 +1015,7 @@ declare global {
1000
1015
  declare global {
1001
1016
  namespace JSX {
1002
1017
  interface IntrinsicElements {
1003
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1018
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1004
1019
  }
1005
1020
  }
1006
1021
  }
@@ -1008,7 +1023,7 @@ declare global {
1008
1023
 
1009
1024
  declare global {
1010
1025
  interface HTMLElementTagNameMap {
1011
- 'gs-aggregate': AggregateComponent;
1026
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1012
1027
  }
1013
1028
  }
1014
1029
 
@@ -1016,7 +1031,7 @@ declare global {
1016
1031
  declare global {
1017
1032
  namespace JSX {
1018
1033
  interface IntrinsicElements {
1019
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1034
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1020
1035
  }
1021
1036
  }
1022
1037
  }
@@ -1040,7 +1055,7 @@ declare global {
1040
1055
 
1041
1056
  declare global {
1042
1057
  interface HTMLElementTagNameMap {
1043
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1058
+ 'gs-sequences-by-location': SequencesByLocationComponent;
1044
1059
  }
1045
1060
  }
1046
1061
 
@@ -1048,7 +1063,7 @@ declare global {
1048
1063
  declare global {
1049
1064
  namespace JSX {
1050
1065
  interface IntrinsicElements {
1051
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1066
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1052
1067
  }
1053
1068
  }
1054
1069
  }
@@ -1056,7 +1071,7 @@ declare global {
1056
1071
 
1057
1072
  declare global {
1058
1073
  interface HTMLElementTagNameMap {
1059
- 'gs-sequences-by-location': SequencesByLocationComponent;
1074
+ 'gs-statistics': StatisticsComponent;
1060
1075
  }
1061
1076
  }
1062
1077
 
@@ -1064,7 +1079,7 @@ declare global {
1064
1079
  declare global {
1065
1080
  namespace JSX {
1066
1081
  interface IntrinsicElements {
1067
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1082
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1068
1083
  }
1069
1084
  }
1070
1085
  }
@@ -1072,7 +1087,7 @@ declare global {
1072
1087
 
1073
1088
  declare global {
1074
1089
  interface HTMLElementTagNameMap {
1075
- 'gs-statistics': StatisticsComponent;
1090
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1076
1091
  }
1077
1092
  }
1078
1093
 
@@ -1080,7 +1095,7 @@ declare global {
1080
1095
  declare global {
1081
1096
  namespace JSX {
1082
1097
  interface IntrinsicElements {
1083
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1098
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1084
1099
  }
1085
1100
  }
1086
1101
  }
@@ -1108,10 +1123,10 @@ declare global {
1108
1123
 
1109
1124
  declare global {
1110
1125
  interface HTMLElementTagNameMap {
1111
- 'gs-location-filter': LocationFilterComponent;
1126
+ 'gs-text-filter': TextFilterComponent;
1112
1127
  }
1113
1128
  interface HTMLElementEventMap {
1114
- [gsEventNames.locationChanged]: LocationChangedEvent;
1129
+ [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1115
1130
  }
1116
1131
  }
1117
1132
 
@@ -1119,7 +1134,7 @@ declare global {
1119
1134
  declare global {
1120
1135
  namespace JSX {
1121
1136
  interface IntrinsicElements {
1122
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1137
+ 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1123
1138
  }
1124
1139
  }
1125
1140
  }
@@ -1127,10 +1142,10 @@ declare global {
1127
1142
 
1128
1143
  declare global {
1129
1144
  interface HTMLElementTagNameMap {
1130
- 'gs-text-filter': TextFilterComponent;
1145
+ 'gs-location-filter': LocationFilterComponent;
1131
1146
  }
1132
1147
  interface HTMLElementEventMap {
1133
- [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1148
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1134
1149
  }
1135
1150
  }
1136
1151
 
@@ -1138,7 +1153,7 @@ declare global {
1138
1153
  declare global {
1139
1154
  namespace JSX {
1140
1155
  interface IntrinsicElements {
1141
- 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1156
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1142
1157
  }
1143
1158
  }
1144
1159
  }
@@ -1182,6 +1197,26 @@ declare global {
1182
1197
  }
1183
1198
 
1184
1199
 
1200
+ declare global {
1201
+ interface HTMLElementTagNameMap {
1202
+ 'gs-number-range-filter': NumberRangeFilterComponent;
1203
+ }
1204
+ interface HTMLElementEventMap {
1205
+ [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1206
+ [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1207
+ }
1208
+ }
1209
+
1210
+
1211
+ declare global {
1212
+ namespace JSX {
1213
+ interface IntrinsicElements {
1214
+ 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1215
+ }
1216
+ }
1217
+ }
1218
+
1219
+
1185
1220
  declare module 'chart.js' {
1186
1221
  interface CartesianScaleTypeRegistry {
1187
1222
  logit: {
package/dist/util.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D, a, L, T, d, g, v } from "./LineageFilterChangedEvent-b0iuroUL.js";
1
+ import { D, a, L, T, d, g, v } from "./LineageFilterChangedEvent-GgkxoF3X.js";
2
2
  export {
3
3
  D as DateRangeOptionChangedEvent,
4
4
  a as LineageFilterChangedEvent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.19.3",
3
+ "version": "0.19.5",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -6,6 +6,7 @@ export interface MinMaxPercentSliderProps {
6
6
  max: number;
7
7
  setMin: (min: number) => void;
8
8
  setMax: (max: number) => void;
9
+ onDrop?: () => void;
9
10
  rangeMin?: number;
10
11
  rangeMax?: number;
11
12
  step?: number;
@@ -16,6 +17,7 @@ export const MinMaxRangeSlider: FunctionComponent<MinMaxPercentSliderProps> = ({
16
17
  max,
17
18
  setMin,
18
19
  setMax,
20
+ onDrop,
19
21
  rangeMin = 0,
20
22
  rangeMax = 100,
21
23
  step = 0.1,
@@ -55,14 +57,16 @@ export const MinMaxRangeSlider: FunctionComponent<MinMaxPercentSliderProps> = ({
55
57
  }
56
58
  };
57
59
 
60
+ const lowerBoundary = getGradientBoundary(min, rangeMin, rangeMax);
61
+ const upperBoundary = getGradientBoundary(max, rangeMin, rangeMax);
58
62
  const background = `
59
63
  linear-gradient(
60
64
  to right,
61
65
  ${sliderColor} 0%,
62
- ${sliderColor} ${min}%,
63
- ${rangeColor} ${min}%,
64
- ${rangeColor} ${max}%,
65
- ${sliderColor} ${max}%,
66
+ ${sliderColor} ${lowerBoundary}%,
67
+ ${rangeColor} ${lowerBoundary}%,
68
+ ${rangeColor} ${upperBoundary}%,
69
+ ${sliderColor} ${upperBoundary}%,
66
70
  ${sliderColor} 100%)
67
71
  `;
68
72
 
@@ -73,6 +77,8 @@ export const MinMaxRangeSlider: FunctionComponent<MinMaxPercentSliderProps> = ({
73
77
  type='range'
74
78
  value={min}
75
79
  onInput={onMinChange}
80
+ onMouseUp={() => onDrop?.()}
81
+ onTouchEnd={() => onDrop?.()}
76
82
  min={`${rangeMin}`}
77
83
  max={`${rangeMax}`}
78
84
  step={step}
@@ -86,8 +92,17 @@ export const MinMaxRangeSlider: FunctionComponent<MinMaxPercentSliderProps> = ({
86
92
  max={`${rangeMax}`}
87
93
  step={step}
88
94
  onInput={onMaxChange}
95
+ onMouseUp={() => onDrop?.()}
96
+ onTouchEnd={() => onDrop?.()}
89
97
  style={{ background, zIndex: zIndexTo }}
90
98
  />
91
99
  </div>
92
100
  );
93
101
  };
102
+
103
+ /**
104
+ * This is a linear function that returns 0 for x = lowerBound and 100 for x = upperBound.
105
+ */
106
+ function getGradientBoundary(x: number, lowerBound: number, upperBound: number) {
107
+ return ((x - lowerBound) / (upperBound - lowerBound)) * 100;
108
+ }
@@ -256,7 +256,10 @@ export const ChangingTheValueProgrammatically: StoryObj<DateRangeFilterProps> =
256
256
 
257
257
  await step('Clearing the value from within the component is still possible', async () => {
258
258
  await waitFor(async () => {
259
- await userEvent.click(canvas.getByRole('button', { name: '×' }));
259
+ await expect(canvas.getByRole('button', { name: '×' })).toBeVisible();
260
+ });
261
+ await userEvent.click(canvas.getByRole('button', { name: '×' }));
262
+ await waitFor(async () => {
260
263
  await expectOptionSelected(canvasElement, placeholder);
261
264
  });
262
265
  await waitFor(async () => {
@@ -10,7 +10,7 @@ const SplicedGeneData = `
10
10
  ##species https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id=641809
11
11
  NC_026431.1 RefSeq region 1 982 . + . ID=NC_026431.1:1..982;Dbxref=taxon:641809;Name=7;collection-date=09-Apr-2009;country=USA: California state;gbkey=Src;genome=genomic;mol_type=viral cRNA;nat-host=Homo sapiens%3B gender M%3B age 54;segment=7;serotype=H1N1;strain=A/California/07/2009
12
12
  NC_026431.1 RefSeq CDS 1 26 . + 0 Name=M2;gene=M2;gbkey=CDS;locus_tag=UJ99_s7gp1;protein_id=YP_009118622.1;product=matrix protein 2;ID=cds-YP_009118622.1;Dbxref=GenBank:YP_009118622.1,GeneID:23308108
13
- NC_026431.1 RefSeq CDS 715 982 . + 1 Name=M2;gene=M2;gbkey=CDS;locus_tag=UJ99_s7gp1;protein_id=YP_009118622.1;product=matrix protein 2;ID=cds-YP_009118622.1;Dbxref=GenBank:YP_009118622.1,GeneID:23308108
13
+ NC_026431.1 RefSeq CDS 715 982 . + 1 Name="M2";gene=M2;gbkey=CDS;locus_tag=UJ99_s7gp1;protein_id=YP_009118622.1;product=matrix protein 2;ID=cds-YP_009118622.1;Dbxref=GenBank:YP_009118622.1,GeneID:23308108
14
14
  NC_026431.1 RefSeq CDS 1 759 . + 0 Name=M1;gene=M1;gbkey=CDS;locus_tag=UJ99_s7gp2;protein_id=YP_009118623.1;product=matrix protein 1;ID=cds-YP_009118623.1;Dbxref=GenBank:YP_009118623.1,GeneID:23308107
15
15
  NC_026431.1 RefSeq CDS 760 790 . + 0 Name=fakeGene;gene=fakeGene;gbkey=CDS;locus_tag=UJ99_s7fake;protein_id=YP_009118624.1;product=None;ID=cds-YP_009118624.1;Dbxref=GenBank:YP_009118624.1,GeneID:23308109
16
16
  `;
@@ -60,6 +60,10 @@ function getAttributes(attributes: string): Map<string, string> {
60
60
  return attrMap;
61
61
  }
62
62
 
63
+ function removeQuotes(input: string) {
64
+ return input.replace(/^['"](.*)['"]$/, '$1');
65
+ }
66
+
63
67
  function getCDSMap(lines: string[], genome_type: string, geneMap: CDSMap): CDSMap {
64
68
  for (const line of lines) {
65
69
  if (line.startsWith('#') || line.trim() === '') {
@@ -73,7 +77,7 @@ function getCDSMap(lines: string[], genome_type: string, geneMap: CDSMap): CDSMa
73
77
 
74
78
  const [, , type, startStr, endStr, , , , attributes] = fields;
75
79
 
76
- if (type.toLowerCase() !== genome_type.toLowerCase()) {
80
+ if (removeQuotes(type.toLowerCase()) !== genome_type.toLowerCase()) {
77
81
  continue;
78
82
  }
79
83
 
@@ -85,16 +89,18 @@ function getCDSMap(lines: string[], genome_type: string, geneMap: CDSMap): CDSMa
85
89
  }
86
90
 
87
91
  const attrPairs = getAttributes(attributes);
88
- const label = attrPairs.get('Name') || attrPairs.get('gene') || attrPairs.get('gene_name');
89
- if (!label) {
92
+ const labelAttribute = attrPairs.get('Name') || attrPairs.get('gene') || attrPairs.get('gene_name');
93
+ if (!labelAttribute) {
90
94
  throw new UserFacingError(
91
95
  'Invalid gff3 source',
92
96
  `No label found for feature: "${line}", must contain label in Name, gene, or gene_name attribute`,
93
97
  );
94
98
  }
95
- const id = attrPairs.get('ID') || label;
96
- if (attrPairs.get('Parent')) {
97
- const parent = attrPairs.get('Parent');
99
+ const label = removeQuotes(labelAttribute);
100
+ const id = removeQuotes(attrPairs.get('ID') || labelAttribute);
101
+ const parentAttribute = attrPairs.get('Parent');
102
+ if (parentAttribute) {
103
+ const parent = removeQuotes(parentAttribute);
98
104
  if (parent && parent in geneMap) {
99
105
  delete geneMap[parent];
100
106
  }
@@ -51,7 +51,9 @@ export function getFilteredMutationOverTimeData({
51
51
  return true;
52
52
  }
53
53
 
54
- if (applySearchFilter(entry.mutation, sequenceType, mutationFilterValue, annotationProvider)) {
54
+ if (
55
+ mutationOrAnnotationDoNotMatchFilter(entry.mutation, sequenceType, mutationFilterValue, annotationProvider)
56
+ ) {
55
57
  return true;
56
58
  }
57
59
 
@@ -67,7 +69,7 @@ export function getFilteredMutationOverTimeData({
67
69
  return filteredData;
68
70
  }
69
71
 
70
- export function applySearchFilter(
72
+ export function mutationOrAnnotationDoNotMatchFilter(
71
73
  mutation: Mutation,
72
74
  sequenceType: SequenceType,
73
75
  filterValue: string,
@@ -0,0 +1,31 @@
1
+ import z from 'zod';
2
+
3
+ import { gsEventNames } from '../../utils/gsEventNames';
4
+
5
+ type LapisNumberFilter = Record<string, number | undefined>;
6
+
7
+ export const numberRangeSchema = z.object({
8
+ min: z.number().optional(),
9
+ max: z.number().optional(),
10
+ });
11
+ export type NumberRange = z.infer<typeof numberRangeSchema>;
12
+
13
+ export class NumberRangeValueChangedEvent extends CustomEvent<NumberRange> {
14
+ constructor(detail: NumberRange) {
15
+ super(gsEventNames.numberRangeValueChanged, {
16
+ detail,
17
+ bubbles: true,
18
+ composed: true,
19
+ });
20
+ }
21
+ }
22
+
23
+ export class NumberRangeFilterChangedEvent extends CustomEvent<LapisNumberFilter> {
24
+ constructor(detail: LapisNumberFilter) {
25
+ super(gsEventNames.numberRangeFilterChanged, {
26
+ detail,
27
+ bubbles: true,
28
+ composed: true,
29
+ });
30
+ }
31
+ }