@genspectrum/dashboard-components 0.6.11 → 0.6.12

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 (34) hide show
  1. package/dist/dashboard-components.js +1072 -853
  2. package/dist/dashboard-components.js.map +1 -1
  3. package/dist/genspectrum-components.d.ts +7 -7
  4. package/dist/style.css +113 -0
  5. package/package.json +2 -2
  6. package/src/preact/components/checkbox-selector.stories.tsx +93 -11
  7. package/src/preact/components/checkbox-selector.tsx +19 -0
  8. package/src/preact/components/color-scale-selector-dropdown.tsx +5 -3
  9. package/src/preact/components/dropdown.tsx +3 -3
  10. package/src/preact/components/mutation-type-selector.stories.tsx +115 -0
  11. package/src/preact/components/mutation-type-selector.tsx +33 -8
  12. package/src/preact/components/percent-input.stories.tsx +93 -0
  13. package/src/preact/components/percent-intput.tsx +4 -0
  14. package/src/preact/components/proportion-selector-dropdown.stories.tsx +2 -2
  15. package/src/preact/components/proportion-selector-dropdown.tsx +9 -7
  16. package/src/preact/components/proportion-selector.stories.tsx +4 -4
  17. package/src/preact/components/proportion-selector.tsx +46 -12
  18. package/src/preact/components/segment-selector.stories.tsx +151 -0
  19. package/src/preact/components/{SegmentSelector.tsx → segment-selector.tsx} +29 -20
  20. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +1 -1
  21. package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
  22. package/src/preact/mutationComparison/queryMutationData.ts +1 -1
  23. package/src/preact/mutations/mutations-grid.tsx +5 -1
  24. package/src/preact/mutations/mutations.tsx +1 -1
  25. package/src/preact/mutations/queryMutations.ts +1 -1
  26. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +4 -4
  27. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +3 -2
  28. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
  29. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +3 -2
  30. package/src/preact/useQuery.ts +1 -1
  31. package/src/query/queryMutationsOverTime.ts +3 -3
  32. package/src/utils/map2d.spec.ts +83 -22
  33. package/src/utils/map2d.ts +158 -0
  34. package/src/utils/Map2d.ts +0 -75
@@ -12,7 +12,7 @@ import {
12
12
  type SubstitutionOrDeletionEntry,
13
13
  type TemporalGranularity,
14
14
  } from '../types';
15
- import { Map2d } from '../utils/Map2d';
15
+ import { Map2dBase, type Map2d } from '../utils/map2d';
16
16
  import { type Deletion, type Substitution } from '../utils/mutations';
17
17
  import {
18
18
  dateRangeCompare,
@@ -154,7 +154,7 @@ function fetchAndPrepareSubstitutionsOrDeletions(filter: LapisFilter, sequenceTy
154
154
  }
155
155
 
156
156
  export function groupByMutation(data: MutationOverTimeData[]) {
157
- const dataArray = new Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>(
157
+ const dataArray = new Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>(
158
158
  (mutation) => mutation.code,
159
159
  (date) => date.toString(),
160
160
  );
@@ -174,7 +174,7 @@ export function groupByMutation(data: MutationOverTimeData[]) {
174
174
  }
175
175
 
176
176
  function addZeroValuesForDatesWithNoMutationData(
177
- dataArray: Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>,
177
+ dataArray: Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>,
178
178
  data: MutationOverTimeData[],
179
179
  ) {
180
180
  if (dataArray.getFirstAxisKeys().length !== 0) {
@@ -1,23 +1,23 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
- import { Map2d } from './Map2d';
3
+ import { Map2dBase, Map2dView } from './map2d';
4
4
 
5
- describe('Map2d', () => {
5
+ describe('Map2dContainer', () => {
6
6
  it('should add a value and return it', () => {
7
- const map2d = new Map2d<string, string, number>();
7
+ const map2d = new Map2dBase<string, string, number>();
8
8
  map2d.set('a', 'b', 2);
9
9
  expect(map2d.get('a', 'b')).toBe(2);
10
10
  });
11
11
 
12
12
  it('should update a value', () => {
13
- const map2d = new Map2d<string, string, number>();
13
+ const map2d = new Map2dBase<string, string, number>();
14
14
  map2d.set('a', 'b', 2);
15
15
  map2d.set('a', 'b', 3);
16
16
  expect(map2d.get('a', 'b')).toBe(3);
17
17
  });
18
18
 
19
19
  it('should return the data as an array', () => {
20
- const map2d = new Map2d<string, string, number>();
20
+ const map2d = new Map2dBase<string, string, number>();
21
21
  map2d.set('a', 'b', 1);
22
22
  map2d.set('a', 'd', 2);
23
23
  map2d.set('c', 'b', 3);
@@ -30,7 +30,7 @@ describe('Map2d', () => {
30
30
  });
31
31
 
32
32
  it('should fill empty values with the given value', () => {
33
- const map2d = new Map2d<string, string, number>();
33
+ const map2d = new Map2dBase<string, string, number>();
34
34
  map2d.set('a', 'b', 2);
35
35
  map2d.set('c', 'd', 4);
36
36
  expect(map2d.getAsArray(0)).toEqual([
@@ -40,7 +40,7 @@ describe('Map2d', () => {
40
40
  });
41
41
 
42
42
  it('should return the keys from the first axis', () => {
43
- const map2d = new Map2d<string, string, number>();
43
+ const map2d = new Map2dBase<string, string, number>();
44
44
  map2d.set('a', 'b', 2);
45
45
  map2d.set('c', 'd', 4);
46
46
 
@@ -48,7 +48,7 @@ describe('Map2d', () => {
48
48
  });
49
49
 
50
50
  it('should return the keys from the second axis', () => {
51
- const map2d = new Map2d<string, string, number>();
51
+ const map2d = new Map2dBase<string, string, number>();
52
52
  map2d.set('a', 'b', 2);
53
53
  map2d.set('c', 'd', 4);
54
54
 
@@ -56,7 +56,7 @@ describe('Map2d', () => {
56
56
  });
57
57
 
58
58
  it('should work with objects as keys', () => {
59
- const map2d = new Map2d<{ a: string }, { b: string }, number>();
59
+ const map2d = new Map2dBase<{ a: string }, { b: string }, number>();
60
60
  map2d.set({ a: 'a' }, { b: 'b' }, 2);
61
61
  map2d.set({ a: 'second' }, { b: 'second' }, 3);
62
62
 
@@ -65,30 +65,91 @@ describe('Map2d', () => {
65
65
  });
66
66
 
67
67
  it('should update a value with objects as keys', () => {
68
- const map2d = new Map2d<{ a: string }, { b: string }, number>();
68
+ const map2d = new Map2dBase<{ a: string }, { b: string }, number>();
69
69
  map2d.set({ a: 'a' }, { b: 'b' }, 2);
70
70
  map2d.set({ a: 'a' }, { b: 'b' }, 3);
71
71
  expect(map2d.get({ a: 'a' }, { b: 'b' })).toBe(3);
72
72
  });
73
73
 
74
- it('should create a deep copy of the map', () => {
75
- const map2d = new Map2d<string, string, number>();
74
+ it('should return a row by key', () => {
75
+ const map2d = new Map2dBase<string, string, number>();
76
76
  map2d.set('a', 'b', 2);
77
- expect(map2d.get('a', 'b')).toBe(2);
77
+ map2d.set('c', 'd', 4);
78
+
79
+ expect(map2d.getRow('a', 0)).toEqual([2, 0]);
80
+ expect(map2d.getRow('c', 0)).toEqual([0, 4]);
81
+ });
82
+
83
+ it('should return an empty array when the row does not exist', () => {
84
+ const map2d = new Map2dBase<string, string, number>();
85
+ map2d.set('a', 'b', 2);
86
+
87
+ expect(map2d.getRow('c', 0)).toEqual([]);
88
+ });
89
+ });
78
90
 
79
- const copy = map2d.copy();
80
- expect(copy.get('a', 'b')).toBe(2);
91
+ describe('Map2dView', () => {
92
+ it('should show the same data as the container', () => {
93
+ const container = createBaseContainer();
81
94
 
82
- map2d.deleteRow('a');
83
- expect(map2d.get('a', 'b')).toBe(undefined);
95
+ const view = new Map2dView<string, string, number>(container);
96
+
97
+ expect(view.get('a', 'b')).toBe(container.get('a', 'b'));
98
+ expect(view.get('a', 'd')).toBe(container.get('a', 'd'));
99
+ expect(view.get('c', 'b')).toBe(container.get('c', 'b'));
100
+ expect(view.get('c', 'd')).toBe(container.get('c', 'd'));
101
+ });
102
+
103
+ it('should remove a row', () => {
104
+ const container = createBaseContainer();
105
+
106
+ const view = new Map2dView<string, string, number>(container);
107
+
108
+ view.deleteRow('a');
109
+
110
+ expect(view.get('a', 'b')).toBeUndefined();
111
+ expect(view.get('a', 'd')).toBeUndefined();
112
+ expect(view.get('c', 'b')).toBe(container.get('c', 'b'));
113
+ expect(view.get('c', 'd')).toBe(container.get('c', 'd'));
114
+ expect(view.getFirstAxisKeys().length).toBe(1);
115
+ expect(view.getFirstAxisKeys()[0]).toBe('c');
116
+ expect(view.getSecondAxisKeys().length).toBe(2);
117
+ });
118
+
119
+ it('should return the view of the data as an array', () => {
120
+ const container = createBaseContainer();
121
+ const view = new Map2dView<string, string, number>(container);
122
+ view.deleteRow('c');
123
+
124
+ expect(view.getAsArray(0)).toEqual([[1, 0]]);
125
+ });
126
+
127
+ it('should throw an error when trying to set a value', () => {
128
+ const container = createBaseContainer();
129
+ const view = new Map2dView<string, string, number>(container);
130
+ expect(() => view.set()).toThrowError();
84
131
  });
85
132
 
86
133
  it('should return a row by key', () => {
87
- const map2d = new Map2d<string, string, number>();
88
- map2d.set('a', 'b', 2);
89
- map2d.set('c', 'd', 4);
134
+ const container = createBaseContainer();
135
+ const view = new Map2dView<string, string, number>(container);
90
136
 
91
- expect(map2d.getRow('a', 0)).toEqual([2, 0]);
92
- expect(map2d.getRow('c', 0)).toEqual([0, 4]);
137
+ expect(view.getRow('a', 0)).toEqual([1, 0]);
93
138
  });
139
+
140
+ it('should return an empty array when the row does not exist', () => {
141
+ const container = createBaseContainer();
142
+ const view = new Map2dView<string, string, number>(container);
143
+ view.deleteRow('c');
144
+
145
+ expect(view.getRow('c', 0)).toEqual([]);
146
+ });
147
+
148
+ function createBaseContainer() {
149
+ const container = new Map2dBase<string, string, number>();
150
+ container.set('a', 'b', 1);
151
+ container.set('c', 'b', 3);
152
+ container.set('c', 'd', 4);
153
+ return container;
154
+ }
94
155
  });
@@ -0,0 +1,158 @@
1
+ import hash from 'object-hash';
2
+
3
+ export interface Map2d<Key1, Key2, Value> {
4
+ get(keyFirstAxis: Key1, keySecondAxis: Key2): Value | undefined;
5
+
6
+ set(keyFirstAxis: Key1, keySecondAxis: Key2, value: Value): void;
7
+
8
+ getRow(key: Key1, fillEmptyWith: Value): Value[];
9
+
10
+ deleteRow(key: Key1): void;
11
+
12
+ getFirstAxisKeys(): Key1[];
13
+
14
+ getSecondAxisKeys(): Key2[];
15
+
16
+ getAsArray(fillEmptyWith: Value): Value[][];
17
+
18
+ serializeFirstAxis(key: Key1): string;
19
+ serializeSecondAxis(key: Key2): string;
20
+
21
+ readonly keysFirstAxis: Map<string, Key1>;
22
+ readonly keysSecondAxis: Map<string, Key2>;
23
+ }
24
+
25
+ export class Map2dBase<Key1 extends object | string, Key2 extends object | string, Value>
26
+ implements Map2d<Key1, Key2, Value>
27
+ {
28
+ readonly data: Map<string, Map<string, Value>> = new Map<string, Map<string, Value>>();
29
+ readonly keysFirstAxis = new Map<string, Key1>();
30
+ readonly keysSecondAxis = new Map<string, Key2>();
31
+
32
+ constructor(
33
+ readonly serializeFirstAxis: (key: Key1) => string = (key) => (typeof key === 'string' ? key : hash(key)),
34
+ readonly serializeSecondAxis: (key: Key2) => string = (key) => (typeof key === 'string' ? key : hash(key)),
35
+ ) {}
36
+
37
+ get(keyFirstAxis: Key1, keySecondAxis: Key2) {
38
+ const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
39
+ const serializedKeySecondAxis = this.serializeSecondAxis(keySecondAxis);
40
+ return this.data.get(serializedKeyFirstAxis)?.get(serializedKeySecondAxis);
41
+ }
42
+
43
+ getRow(key: Key1, fillEmptyWith: Value) {
44
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
45
+ const row = this.data.get(serializedKeyFirstAxis);
46
+ if (row === undefined) {
47
+ return [];
48
+ }
49
+ return Array.from(this.keysSecondAxis.keys()).map((key) => row.get(key) ?? fillEmptyWith);
50
+ }
51
+
52
+ set(keyFirstAxis: Key1, keySecondAxis: Key2, value: Value) {
53
+ const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
54
+ const serializedKeySecondAxis = this.serializeSecondAxis(keySecondAxis);
55
+
56
+ if (!this.data.has(serializedKeyFirstAxis)) {
57
+ this.data.set(serializedKeyFirstAxis, new Map<string, Value>());
58
+ }
59
+
60
+ this.data.get(serializedKeyFirstAxis)!.set(serializedKeySecondAxis, value);
61
+
62
+ this.keysFirstAxis.set(serializedKeyFirstAxis, keyFirstAxis);
63
+ this.keysSecondAxis.set(serializedKeySecondAxis, keySecondAxis);
64
+ }
65
+
66
+ deleteRow(key: Key1) {
67
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
68
+ this.data.delete(serializedKeyFirstAxis);
69
+ this.keysFirstAxis.delete(serializedKeyFirstAxis);
70
+ }
71
+
72
+ getFirstAxisKeys() {
73
+ return Array.from(this.keysFirstAxis.values());
74
+ }
75
+
76
+ getSecondAxisKeys() {
77
+ return Array.from(this.keysSecondAxis.values());
78
+ }
79
+
80
+ getAsArray(fillEmptyWith: Value) {
81
+ return this.getFirstAxisKeys().map((firstAxisKey) => {
82
+ return this.getSecondAxisKeys().map((secondAxisKey) => {
83
+ return this.get(firstAxisKey, secondAxisKey) ?? fillEmptyWith;
84
+ });
85
+ });
86
+ }
87
+ }
88
+
89
+ export class Map2dView<Key1 extends object | string, Key2 extends object | string, Value>
90
+ implements Map2d<Key1, Key2, Value>
91
+ {
92
+ readonly keysFirstAxis;
93
+ readonly keysSecondAxis;
94
+
95
+ readonly baseMap: Map2d<Key1, Key2, Value>;
96
+
97
+ constructor(map: Map2d<Key1, Key2, Value>) {
98
+ this.keysFirstAxis = new Map(map.keysFirstAxis);
99
+ this.keysSecondAxis = new Map(map.keysSecondAxis);
100
+
101
+ if (map instanceof Map2dView) {
102
+ this.baseMap = map.baseMap;
103
+ }
104
+ this.baseMap = map;
105
+ }
106
+
107
+ serializeFirstAxis(key: Key1) {
108
+ return this.baseMap.serializeFirstAxis(key);
109
+ }
110
+
111
+ serializeSecondAxis(key: Key2) {
112
+ return this.baseMap.serializeSecondAxis(key);
113
+ }
114
+
115
+ deleteRow(key: Key1) {
116
+ this.keysFirstAxis.delete(this.serializeFirstAxis(key));
117
+ }
118
+
119
+ get(keyFirstAxis: Key1, keySecondAxis: Key2) {
120
+ const firstAxisKey = this.serializeFirstAxis(keyFirstAxis);
121
+ const secondAxisKey = this.serializeSecondAxis(keySecondAxis);
122
+
123
+ if (!this.keysFirstAxis.has(firstAxisKey) || !this.keysSecondAxis.has(secondAxisKey)) {
124
+ return undefined;
125
+ }
126
+
127
+ return this.baseMap.get(keyFirstAxis, keySecondAxis);
128
+ }
129
+
130
+ set() {
131
+ throw new Error('Cannot set value on a Map2dView');
132
+ }
133
+
134
+ getFirstAxisKeys() {
135
+ return Array.from(this.keysFirstAxis.values());
136
+ }
137
+
138
+ getSecondAxisKeys() {
139
+ return Array.from(this.keysSecondAxis.values());
140
+ }
141
+
142
+ getAsArray(fillEmptyWith: Value) {
143
+ return this.getFirstAxisKeys().map((firstAxisKey) => {
144
+ return this.getSecondAxisKeys().map((secondAxisKey) => {
145
+ return this.baseMap.get(firstAxisKey, secondAxisKey) ?? fillEmptyWith;
146
+ });
147
+ });
148
+ }
149
+
150
+ getRow(key: Key1, fillEmptyWith: Value) {
151
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
152
+ if (!this.keysFirstAxis.has(serializedKeyFirstAxis)) {
153
+ return [];
154
+ }
155
+
156
+ return this.baseMap.getRow(key, fillEmptyWith);
157
+ }
158
+ }
@@ -1,75 +0,0 @@
1
- import hash from 'object-hash';
2
-
3
- export class Map2d<Key1 extends object | string, Key2 extends object | string, Value> {
4
- readonly data: Map<string, Map<string, Value>> = new Map<string, Map<string, Value>>();
5
- readonly keysFirstAxis = new Map<string, Key1>();
6
- readonly keysSecondAxis = new Map<string, Key2>();
7
-
8
- constructor(
9
- readonly serializeFirstAxis: (key: Key1) => string = (key) => (typeof key === 'string' ? key : hash(key)),
10
- readonly serializeSecondAxis: (key: Key2) => string = (key) => (typeof key === 'string' ? key : hash(key)),
11
- ) {}
12
-
13
- get(keyFirstAxis: Key1, keySecondAxis: Key2) {
14
- const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
15
- const serializedKeySecondAxis = this.serializeSecondAxis(keySecondAxis);
16
- return this.data.get(serializedKeyFirstAxis)?.get(serializedKeySecondAxis);
17
- }
18
-
19
- getRow(key: Key1, fillEmptyWith: Value) {
20
- const serializedKeyFirstAxis = this.serializeFirstAxis(key);
21
- const row = this.data.get(serializedKeyFirstAxis);
22
- if (row === undefined) {
23
- return [];
24
- }
25
- return Array.from(this.keysSecondAxis.keys()).map((key) => row.get(key) ?? fillEmptyWith);
26
- }
27
-
28
- set(keyFirstAxis: Key1, keySecondAxis: Key2, value: Value) {
29
- const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
30
- const serializedKeySecondAxis = this.serializeSecondAxis(keySecondAxis);
31
-
32
- if (!this.data.has(serializedKeyFirstAxis)) {
33
- this.data.set(serializedKeyFirstAxis, new Map<string, Value>());
34
- }
35
-
36
- this.data.get(serializedKeyFirstAxis)!.set(serializedKeySecondAxis, value);
37
-
38
- this.keysFirstAxis.set(serializedKeyFirstAxis, keyFirstAxis);
39
- this.keysSecondAxis.set(serializedKeySecondAxis, keySecondAxis);
40
- }
41
-
42
- deleteRow(key: Key1) {
43
- const serializedKeyFirstAxis = this.serializeFirstAxis(key);
44
- this.data.delete(serializedKeyFirstAxis);
45
- this.keysFirstAxis.delete(serializedKeyFirstAxis);
46
- }
47
-
48
- getFirstAxisKeys() {
49
- return Array.from(this.keysFirstAxis.values());
50
- }
51
-
52
- getSecondAxisKeys() {
53
- return Array.from(this.keysSecondAxis.values());
54
- }
55
-
56
- getAsArray(fillEmptyWith: Value) {
57
- return this.getFirstAxisKeys().map((firstAxisKey) => {
58
- return this.getSecondAxisKeys().map((secondAxisKey) => {
59
- return this.get(firstAxisKey, secondAxisKey) ?? fillEmptyWith;
60
- });
61
- });
62
- }
63
-
64
- copy() {
65
- const copy = new Map2d<Key1, Key2, Value>(this.serializeFirstAxis, this.serializeSecondAxis);
66
- this.data.forEach((value, key) => {
67
- const keyFirstAxis = this.keysFirstAxis.get(key);
68
- value.forEach((value, key) => {
69
- const keySecondAxis = this.keysSecondAxis.get(key);
70
- copy.set(keyFirstAxis!, keySecondAxis!, value);
71
- });
72
- });
73
- return copy;
74
- }
75
- }