@genspectrum/dashboard-components 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/util.d.ts CHANGED
@@ -838,7 +838,11 @@ declare global {
838
838
 
839
839
  declare global {
840
840
  interface HTMLElementTagNameMap {
841
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
841
+ 'gs-date-range-selector': DateRangeSelectorComponent;
842
+ }
843
+ interface HTMLElementEventMap {
844
+ 'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
845
+ 'gs-date-range-option-changed': DateRangeOptionChangedEvent;
842
846
  }
843
847
  }
844
848
 
@@ -846,7 +850,7 @@ declare global {
846
850
  declare global {
847
851
  namespace JSX {
848
852
  interface IntrinsicElements {
849
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
853
+ 'gs-date-range-selector': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
850
854
  }
851
855
  }
852
856
  }
@@ -854,7 +858,10 @@ declare global {
854
858
 
855
859
  declare global {
856
860
  interface HTMLElementTagNameMap {
857
- 'gs-mutation-comparison-component': MutationComparisonComponent;
861
+ 'gs-location-filter': LocationFilterComponent;
862
+ }
863
+ interface HTMLElementEventMap {
864
+ 'gs-location-changed': LocationChangedEvent;
858
865
  }
859
866
  }
860
867
 
@@ -862,7 +869,7 @@ declare global {
862
869
  declare global {
863
870
  namespace JSX {
864
871
  interface IntrinsicElements {
865
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
872
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
866
873
  }
867
874
  }
868
875
  }
@@ -870,7 +877,10 @@ declare global {
870
877
 
871
878
  declare global {
872
879
  interface HTMLElementTagNameMap {
873
- 'gs-mutations-component': MutationsComponent;
880
+ 'gs-text-input': TextInputComponent;
881
+ }
882
+ interface HTMLElementEventMap {
883
+ 'gs-text-input-changed': TextInputChangedEvent;
874
884
  }
875
885
  }
876
886
 
@@ -878,7 +888,7 @@ declare global {
878
888
  declare global {
879
889
  namespace JSX {
880
890
  interface IntrinsicElements {
881
- 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
891
+ 'gs-text-input': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
882
892
  }
883
893
  }
884
894
  }
@@ -886,7 +896,10 @@ declare global {
886
896
 
887
897
  declare global {
888
898
  interface HTMLElementTagNameMap {
889
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
899
+ 'gs-mutation-filter': MutationFilterComponent;
900
+ }
901
+ interface HTMLElementEventMap {
902
+ 'gs-mutation-filter-changed': CustomEvent<MutationsFilter>;
890
903
  }
891
904
  }
892
905
 
@@ -894,7 +907,7 @@ declare global {
894
907
  declare global {
895
908
  namespace JSX {
896
909
  interface IntrinsicElements {
897
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
910
+ 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
898
911
  }
899
912
  }
900
913
  }
@@ -902,7 +915,10 @@ declare global {
902
915
 
903
916
  declare global {
904
917
  interface HTMLElementTagNameMap {
905
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
918
+ 'gs-lineage-filter': LineageFilterComponent;
919
+ }
920
+ interface HTMLElementEventMap {
921
+ 'gs-lineage-filter-changed': LineageFilterChangedEvent;
906
922
  }
907
923
  }
908
924
 
@@ -910,7 +926,7 @@ declare global {
910
926
  declare global {
911
927
  namespace JSX {
912
928
  interface IntrinsicElements {
913
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
929
+ 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
914
930
  }
915
931
  }
916
932
  }
@@ -918,7 +934,7 @@ declare global {
918
934
 
919
935
  declare global {
920
936
  interface HTMLElementTagNameMap {
921
- 'gs-aggregate': AggregateComponent;
937
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
922
938
  }
923
939
  }
924
940
 
@@ -926,7 +942,7 @@ declare global {
926
942
  declare global {
927
943
  namespace JSX {
928
944
  interface IntrinsicElements {
929
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
945
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
930
946
  }
931
947
  }
932
948
  }
@@ -934,7 +950,7 @@ declare global {
934
950
 
935
951
  declare global {
936
952
  interface HTMLElementTagNameMap {
937
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
953
+ 'gs-mutation-comparison-component': MutationComparisonComponent;
938
954
  }
939
955
  }
940
956
 
@@ -942,7 +958,7 @@ declare global {
942
958
  declare global {
943
959
  namespace JSX {
944
960
  interface IntrinsicElements {
945
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
961
+ 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
946
962
  }
947
963
  }
948
964
  }
@@ -950,7 +966,7 @@ declare global {
950
966
 
951
967
  declare global {
952
968
  interface HTMLElementTagNameMap {
953
- 'gs-sequences-by-location': SequencesByLocationComponent;
969
+ 'gs-mutations-component': MutationsComponent;
954
970
  }
955
971
  }
956
972
 
@@ -958,7 +974,7 @@ declare global {
958
974
  declare global {
959
975
  namespace JSX {
960
976
  interface IntrinsicElements {
961
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
977
+ 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
962
978
  }
963
979
  }
964
980
  }
@@ -966,7 +982,7 @@ declare global {
966
982
 
967
983
  declare global {
968
984
  interface HTMLElementTagNameMap {
969
- 'gs-mutations-over-time': MutationsOverTimeComponent;
985
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
970
986
  }
971
987
  }
972
988
 
@@ -974,7 +990,7 @@ declare global {
974
990
  declare global {
975
991
  namespace JSX {
976
992
  interface IntrinsicElements {
977
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
993
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
978
994
  }
979
995
  }
980
996
  }
@@ -982,7 +998,7 @@ declare global {
982
998
 
983
999
  declare global {
984
1000
  interface HTMLElementTagNameMap {
985
- 'gs-statistics': StatisticsComponent;
1001
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
986
1002
  }
987
1003
  }
988
1004
 
@@ -990,7 +1006,7 @@ declare global {
990
1006
  declare global {
991
1007
  namespace JSX {
992
1008
  interface IntrinsicElements {
993
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1009
+ 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
994
1010
  }
995
1011
  }
996
1012
  }
@@ -998,11 +1014,7 @@ declare global {
998
1014
 
999
1015
  declare global {
1000
1016
  interface HTMLElementTagNameMap {
1001
- 'gs-date-range-selector': DateRangeSelectorComponent;
1002
- }
1003
- interface HTMLElementEventMap {
1004
- 'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
1005
- 'gs-date-range-option-changed': DateRangeOptionChangedEvent;
1017
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1006
1018
  }
1007
1019
  }
1008
1020
 
@@ -1010,7 +1022,7 @@ declare global {
1010
1022
  declare global {
1011
1023
  namespace JSX {
1012
1024
  interface IntrinsicElements {
1013
- 'gs-date-range-selector': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1025
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1014
1026
  }
1015
1027
  }
1016
1028
  }
@@ -1018,10 +1030,7 @@ declare global {
1018
1030
 
1019
1031
  declare global {
1020
1032
  interface HTMLElementTagNameMap {
1021
- 'gs-location-filter': LocationFilterComponent;
1022
- }
1023
- interface HTMLElementEventMap {
1024
- 'gs-location-changed': LocationChangedEvent;
1033
+ 'gs-aggregate': AggregateComponent;
1025
1034
  }
1026
1035
  }
1027
1036
 
@@ -1029,7 +1038,7 @@ declare global {
1029
1038
  declare global {
1030
1039
  namespace JSX {
1031
1040
  interface IntrinsicElements {
1032
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1041
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1033
1042
  }
1034
1043
  }
1035
1044
  }
@@ -1037,10 +1046,7 @@ declare global {
1037
1046
 
1038
1047
  declare global {
1039
1048
  interface HTMLElementTagNameMap {
1040
- 'gs-text-input': TextInputComponent;
1041
- }
1042
- interface HTMLElementEventMap {
1043
- 'gs-text-input-changed': TextInputChangedEvent;
1049
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
1044
1050
  }
1045
1051
  }
1046
1052
 
@@ -1048,7 +1054,7 @@ declare global {
1048
1054
  declare global {
1049
1055
  namespace JSX {
1050
1056
  interface IntrinsicElements {
1051
- 'gs-text-input': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1057
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1052
1058
  }
1053
1059
  }
1054
1060
  }
@@ -1056,10 +1062,7 @@ declare global {
1056
1062
 
1057
1063
  declare global {
1058
1064
  interface HTMLElementTagNameMap {
1059
- 'gs-mutation-filter': MutationFilterComponent;
1060
- }
1061
- interface HTMLElementEventMap {
1062
- 'gs-mutation-filter-changed': CustomEvent<MutationsFilter>;
1065
+ 'gs-sequences-by-location': SequencesByLocationComponent;
1063
1066
  }
1064
1067
  }
1065
1068
 
@@ -1067,7 +1070,7 @@ declare global {
1067
1070
  declare global {
1068
1071
  namespace JSX {
1069
1072
  interface IntrinsicElements {
1070
- 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1073
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1071
1074
  }
1072
1075
  }
1073
1076
  }
@@ -1075,10 +1078,7 @@ declare global {
1075
1078
 
1076
1079
  declare global {
1077
1080
  interface HTMLElementTagNameMap {
1078
- 'gs-lineage-filter': LineageFilterComponent;
1079
- }
1080
- interface HTMLElementEventMap {
1081
- 'gs-lineage-filter-changed': LineageFilterChangedEvent;
1081
+ 'gs-statistics': StatisticsComponent;
1082
1082
  }
1083
1083
  }
1084
1084
 
@@ -1086,7 +1086,7 @@ declare global {
1086
1086
  declare global {
1087
1087
  namespace JSX {
1088
1088
  interface IntrinsicElements {
1089
- 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1089
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1090
1090
  }
1091
1091
  }
1092
1092
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -120,26 +120,24 @@ export function DownshiftCombobox<Item>({
120
120
  </button>
121
121
  </div>
122
122
  </div>
123
- {isOpen && (
124
- <ul
125
- className='absolute bg-white mt-1 shadow-md max-h-80 overflow-scroll z-10 w-full min-w-32'
126
- {...getMenuProps()}
127
- >
128
- {items.length > 0 ? (
129
- items.map((item, index) => (
130
- <li
131
- className={`${highlightedIndex === index ? 'bg-blue-300' : ''} ${selectedItem !== null && itemToString(selectedItem) === itemToString(item) ? 'font-bold' : ''} py-2 px-3 shadow-sm flex flex-col`}
132
- key={itemToString(item)}
133
- {...getItemProps({ item, index })}
134
- >
135
- {formatItemInList(item)}
136
- </li>
137
- ))
138
- ) : (
139
- <li className='py-2 px-3 shadow-sm flex flex-col'>No elements to select.</li>
140
- )}
141
- </ul>
142
- )}
123
+ <ul
124
+ className={`absolute bg-white mt-1 shadow-md max-h-80 overflow-scroll z-10 w-full min-w-32 ${isOpen ? '' : 'hidden'}`}
125
+ {...getMenuProps()}
126
+ >
127
+ {items.length > 0 ? (
128
+ items.map((item, index) => (
129
+ <li
130
+ className={`${highlightedIndex === index ? 'bg-blue-300' : ''} ${selectedItem !== null && itemToString(selectedItem) === itemToString(item) ? 'font-bold' : ''} py-2 px-3 shadow-sm flex flex-col`}
131
+ key={itemToString(item)}
132
+ {...getItemProps({ item, index })}
133
+ >
134
+ {formatItemInList(item)}
135
+ </li>
136
+ ))
137
+ ) : (
138
+ <li className='py-2 px-3 shadow-sm flex flex-col'>No elements to select.</li>
139
+ )}
140
+ </ul>
143
141
  </div>
144
142
  );
145
143
  }
@@ -0,0 +1,34 @@
1
+ import { describe, expect, test } from 'vitest';
2
+
3
+ import { fetchStringAutocompleteList } from './fetchStringAutocompleteList';
4
+ import { DUMMY_LAPIS_URL, lapisRequestMocks } from '../../../vitest.setup';
5
+
6
+ describe('fetchStringAutocompleteList', () => {
7
+ test('should fetch autocompletion list and sort by field value', async () => {
8
+ const field = 'host';
9
+
10
+ lapisRequestMocks.aggregated(
11
+ { fields: [field], country: 'Germany' },
12
+ {
13
+ data: [
14
+ { count: 1, host: 'host_c' },
15
+ { count: 2, host: 'host_b' },
16
+ { count: 3, host: null },
17
+ { count: 4, host: 'host_a' },
18
+ ],
19
+ },
20
+ );
21
+
22
+ const result = await fetchStringAutocompleteList({
23
+ field,
24
+ lapis: DUMMY_LAPIS_URL,
25
+ lapisFilter: { country: 'Germany' },
26
+ });
27
+
28
+ expect(result).to.deep.equal([
29
+ { count: 4, value: 'host_a' },
30
+ { count: 2, value: 'host_b' },
31
+ { count: 1, value: 'host_c' },
32
+ ]);
33
+ });
34
+ });
@@ -12,9 +12,23 @@ export async function fetchStringAutocompleteList({
12
12
  lapisFilter?: LapisFilter;
13
13
  signal?: AbortSignal;
14
14
  }) {
15
- const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string>>(lapisFilter ?? {}, [field]);
15
+ const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string | null>>(lapisFilter ?? {}, [
16
+ field,
17
+ ]);
16
18
 
17
19
  const data = (await fetchAggregatedOperator.evaluate(lapis, signal)).content;
18
20
 
19
- return data.map((item) => item[field]).sort();
21
+ return data
22
+ .map((item) => ({ count: item.count, value: item[field] }))
23
+ .filter((item): item is { count: number; value: string } => item.value !== null)
24
+ .sort((a, b) => {
25
+ if (a.value === null) {
26
+ return 1;
27
+ }
28
+ if (b.value === null) {
29
+ return -1;
30
+ }
31
+
32
+ return a.value.localeCompare(b.value);
33
+ });
20
34
  }
@@ -68,32 +68,46 @@ const TextInputInner: FunctionComponent<TextInputInnerProps> = ({
68
68
  return <TextSelector lapisField={lapisField} value={value} placeholderText={placeholderText} data={data} />;
69
69
  };
70
70
 
71
+ type SelectItem = {
72
+ count: number;
73
+ value: string;
74
+ };
75
+
71
76
  const TextSelector = ({
72
77
  lapisField,
73
78
  value,
74
79
  placeholderText,
75
80
  data,
76
81
  }: TextSelectorProps & {
77
- data: string[];
82
+ data: SelectItem[];
78
83
  }) => {
84
+ const initialSelectedItem = data.find((candidate) => candidate.value == value);
85
+
79
86
  return (
80
87
  <DownshiftCombobox
81
88
  allItems={data}
82
- value={value}
89
+ value={initialSelectedItem}
83
90
  filterItemsByInputValue={filterByInputValue}
84
- createEvent={(item: string | null) => new TextInputChangedEvent({ [lapisField]: item ?? undefined })}
85
- itemToString={(item: string | undefined | null) => item ?? ''}
91
+ createEvent={(item: SelectItem | null) =>
92
+ new TextInputChangedEvent({ [lapisField]: item?.value ?? undefined })
93
+ }
94
+ itemToString={(item: SelectItem | undefined | null) => item?.value ?? ''}
86
95
  placeholderText={placeholderText}
87
- formatItemInList={(item: string) => {
88
- return <span>{item}</span>;
96
+ formatItemInList={(item: SelectItem) => {
97
+ return (
98
+ <p>
99
+ <span>{item.value}</span>
100
+ <span className='ml-2 text-gray-500'>({item.count})</span>
101
+ </p>
102
+ );
89
103
  }}
90
104
  />
91
105
  );
92
106
  };
93
107
 
94
- function filterByInputValue(item: string, inputValue: string | undefined | null) {
108
+ function filterByInputValue(item: SelectItem, inputValue: string | undefined | null) {
95
109
  if (inputValue === undefined || inputValue === null || inputValue === '') {
96
110
  return true;
97
111
  }
98
- return item?.toLowerCase().includes(inputValue?.toLowerCase() || '');
112
+ return item.value?.toLowerCase().includes(inputValue?.toLowerCase() || '');
99
113
  }
@@ -35745,10 +35745,10 @@ function um({
35745
35745
  ]
35746
35746
  }
35747
35747
  ) }),
35748
- _ && /* @__PURE__ */ v(
35748
+ /* @__PURE__ */ v(
35749
35749
  "ul",
35750
35750
  {
35751
- className: "absolute bg-white mt-1 shadow-md max-h-80 overflow-scroll z-10 w-full min-w-32",
35751
+ className: `absolute bg-white mt-1 shadow-md max-h-80 overflow-scroll z-10 w-full min-w-32 ${_ ? "" : "hidden"}`,
35752
35752
  ...x(),
35753
35753
  children: c.length > 0 ? c.map((Z, _t) => /* @__PURE__ */ v(
35754
35754
  "li",
@@ -35870,7 +35870,9 @@ async function l$({
35870
35870
  lapisFilter: e,
35871
35871
  signal: i
35872
35872
  }) {
35873
- return (await new Xr(e ?? {}, [t]).evaluate(n, i)).content.map((o) => o[t]).sort();
35873
+ return (await new Xr(e ?? {}, [
35874
+ t
35875
+ ]).evaluate(n, i)).content.map((o) => ({ count: o.count, value: o[t] })).filter((o) => o.value !== null).sort((o, l) => o.value === null ? 1 : l.value === null ? -1 : o.value.localeCompare(l.value));
35874
35876
  }
35875
35877
  const c$ = O.object({
35876
35878
  lapisField: O.string().min(1),
@@ -35901,20 +35903,31 @@ const c$ = O.object({
35901
35903
  value: t,
35902
35904
  placeholderText: e,
35903
35905
  data: i
35904
- }) => /* @__PURE__ */ v(
35905
- um,
35906
- {
35907
- allItems: i,
35908
- value: t,
35909
- filterItemsByInputValue: m$,
35910
- createEvent: (r) => new $k({ [n]: r ?? void 0 }),
35911
- itemToString: (r) => r ?? "",
35912
- placeholderText: e,
35913
- formatItemInList: (r) => /* @__PURE__ */ v("span", { children: r })
35914
- }
35915
- );
35906
+ }) => {
35907
+ const r = i.find((s) => s.value == t);
35908
+ return /* @__PURE__ */ v(
35909
+ um,
35910
+ {
35911
+ allItems: i,
35912
+ value: r,
35913
+ filterItemsByInputValue: m$,
35914
+ createEvent: (s) => new $k({ [n]: (s == null ? void 0 : s.value) ?? void 0 }),
35915
+ itemToString: (s) => (s == null ? void 0 : s.value) ?? "",
35916
+ placeholderText: e,
35917
+ formatItemInList: (s) => /* @__PURE__ */ v("p", { children: [
35918
+ /* @__PURE__ */ v("span", { children: s.value }),
35919
+ /* @__PURE__ */ v("span", { className: "ml-2 text-gray-500", children: [
35920
+ "(",
35921
+ s.count,
35922
+ ")"
35923
+ ] })
35924
+ ] })
35925
+ }
35926
+ );
35927
+ };
35916
35928
  function m$(n, t) {
35917
- return t == null || t === "" ? !0 : n == null ? void 0 : n.toLowerCase().includes((t == null ? void 0 : t.toLowerCase()) || "");
35929
+ var e;
35930
+ return t == null || t === "" ? !0 : (e = n.value) == null ? void 0 : e.toLowerCase().includes((t == null ? void 0 : t.toLowerCase()) || "");
35918
35931
  }
35919
35932
  var g$ = Object.defineProperty, _$ = Object.getOwnPropertyDescriptor, ra = (n, t, e, i) => {
35920
35933
  for (var r = i > 1 ? void 0 : i ? _$(t, e) : t, s = n.length - 1, o; s >= 0; s--)